["A", "A::B", "A::B::C"]
+ def find_ancestor_local_symbol(symbol)
+ each_ancestor do |m|
+ res = m.find_local_symbol(symbol)
+ return res if res
+ end
- def fully_qualified_nesting_namespaces
- return nesting_namespaces if nesting_namespaces.length < 2
- @fqns ||= nesting_namespaces.inject([]) do |list, n|
- list << (list.empty? ? n : "#{list.last}::#{n}")
+ nil
end
- end
- ##
- # TODO: filter included items by #display?
+ ##
+ # Finds a class or module with +name+ in this namespace or its descendants
- def marshal_dump # :nodoc:
- attrs = attributes.sort.map do |attr|
- next unless attr.display?
- [ attr.name, attr.rw,
- attr.visibility, attr.singleton, attr.file_name,
- ]
- end.compact
-
- method_types = methods_by_type.map do |type, visibilities|
- visibilities = visibilities.map do |visibility, methods|
- method_names = methods.map do |method|
- next unless method.display?
- [method.name, method.file_name]
- end.compact
-
- [visibility, method_names.uniq]
- end
-
- [type, visibilities]
- end
-
- [ MARSHAL_VERSION,
- @name,
- full_name,
- @superclass,
- parse(@comment_location),
- attrs,
- constants.select { |constant| constant.display? },
- includes.map do |incl|
- next unless incl.display?
- [incl.name, parse(incl.comment), incl.file_name]
- end.compact,
- method_types,
- extends.map do |ext|
- next unless ext.display?
- [ext.name, parse(ext.comment), ext.file_name]
- end.compact,
- @sections.values,
- @in_files.map do |tl|
- tl.relative_name
- end,
- parent.full_name,
- parent.class,
- ]
- end
+ def find_class_named(name)
+ return self if full_name == name
+ return self if @name == name
- def marshal_load(array) # :nodoc:
- initialize_visibility
- initialize_methods_etc
- @current_section = nil
- @document_self = true
- @done_documenting = false
- @parent = nil
- @temporary_section = nil
- @classes = {}
- @modules = {}
-
- @name = array[1]
- @full_name = array[2]
- @superclass = array[3]
- document = array[4]
-
- @comment = RDoc::Comment.from_document document
-
- @comment_location = if document.parts.first.is_a?(RDoc::Markup::Document)
- document.parts.group_by(&:file)
- else
- { document.file => [document] }
- end
-
- array[5].each do |name, rw, visibility, singleton, file|
- singleton ||= false
- visibility ||= :public
-
- attr = RDoc::Attr.new name, rw, nil, singleton: singleton
-
- add_attribute attr
- attr.visibility = visibility
- attr.record_location RDoc::TopLevel.new file
- end
-
- array[6].each do |constant, document, file|
- case constant
- when RDoc::Constant
- add_constant constant
- else
- constant = add_constant RDoc::Constant.new(constant, nil, RDoc::Comment.from_document(document))
- constant.record_location RDoc::TopLevel.new file
+ @classes.values.find do |klass|
+ next if klass == self
+ klass.find_class_named name
end
end
- array[7].each do |name, document, file|
- incl = add_include RDoc::Include.new(name, RDoc::Comment.from_document(document))
- incl.record_location RDoc::TopLevel.new file
- end
+ ##
+ # Return the fully qualified name of this class or module
- array[8].each do |type, visibilities|
- visibilities.each do |visibility, methods|
- methods.each do |name, file|
- method = RDoc::AnyMethod.new name, singleton: type == 'class'
- method.record_location RDoc::TopLevel.new file
- method.visibility = visibility
- add_method method
- end
- end
+ def full_name
+ @full_name ||= if ClassModule === parent
+ "#{parent.full_name}::#{@name}"
+ else
+ @name
+ end
end
- array[9].each do |name, document, file|
- ext = add_extend RDoc::Extend.new(name, RDoc::Comment.from_document(document))
- ext.record_location RDoc::TopLevel.new file
- end if array[9] # Support Marshal version 1
+ ##
+ # Return array of full_name splitted by +::+.
- sections = (array[10] || []).map do |section|
- [section.title, section]
+ def nesting_namespaces
+ @namespaces ||= full_name.split("::").reject(&:empty?)
end
- @sections = Hash[*sections.flatten]
- @current_section = add_section nil
+ ##
+ # Return array of fully qualified nesting namespaces.
+ #
+ # For example, if full_name is +A::B::C+, this method returns ["A", "A::B", "A::B::C"]
- @in_files = []
-
- (array[11] || []).each do |filename|
- record_location RDoc::TopLevel.new filename
+ def fully_qualified_nesting_namespaces
+ return nesting_namespaces if nesting_namespaces.length < 2
+ @fqns ||= nesting_namespaces.inject([]) do |list, n|
+ list << (list.empty? ? n : "#{list.last}::#{n}")
+ end
end
- @parent_name = array[12]
- @parent_class = array[13]
- end
+ ##
+ # TODO: filter included items by #display?
- ##
- # Merges +class_module+ into this ClassModule.
- #
- # The data in +class_module+ is preferred over the receiver.
+ def marshal_dump # :nodoc:
+ attrs = attributes.sort.map do |attr|
+ next unless attr.display?
+ [ attr.name, attr.rw,
+ attr.visibility, attr.singleton, attr.file_name,
+ ]
+ end.compact
+
+ method_types = methods_by_type.map do |type, visibilities|
+ visibilities = visibilities.map do |visibility, methods|
+ method_names = methods.map do |method|
+ next unless method.display?
+ [method.name, method.file_name]
+ end.compact
- def merge(class_module)
- @parent = class_module.parent
- @parent_name = class_module.parent_name
+ [visibility, method_names.uniq]
+ end
- other_document = parse class_module.comment_location
+ [type, visibilities]
+ end
+
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @superclass,
+ parse(@comment_location),
+ attrs,
+ constants.select { |constant| constant.display? },
+ includes.map do |incl|
+ next unless incl.display?
+ [incl.name, parse(incl.comment), incl.file_name]
+ end.compact,
+ method_types,
+ extends.map do |ext|
+ next unless ext.display?
+ [ext.name, parse(ext.comment), ext.file_name]
+ end.compact,
+ @sections.values,
+ @in_files.map do |tl|
+ tl.relative_name
+ end,
+ parent.full_name,
+ parent.class,
+ ]
+ end
- if other_document
- document = parse @comment_location
+ def marshal_load(array) # :nodoc:
+ initialize_visibility
+ initialize_methods_etc
+ @current_section = nil
+ @document_self = true
+ @done_documenting = false
+ @parent = nil
+ @temporary_section = nil
+ @classes = {}
+ @modules = {}
- document = document.merge other_document
+ @name = array[1]
+ @full_name = array[2]
+ @superclass = array[3]
+ document = array[4]
- @comment = RDoc::Comment.from_document(document)
+ @comment = Comment.from_document document
- @comment_location = if document.parts.first.is_a?(RDoc::Markup::Document)
+ @comment_location = if document.parts.first.is_a?(Markup::Document)
document.parts.group_by(&:file)
else
{ document.file => [document] }
end
- end
- cm = class_module
- other_files = cm.in_files
+ array[5].each do |name, rw, visibility, singleton, file|
+ singleton ||= false
+ visibility ||= :public
+
+ attr = Attr.new name, rw, nil, singleton: singleton
- merge_collections attributes, cm.attributes, other_files do |add, attr|
- if add
add_attribute attr
- else
- @attributes.delete attr
- @methods_hash.delete attr.pretty_name
+ attr.visibility = visibility
+ attr.record_location TopLevel.new file
end
- end
- merge_collections constants, cm.constants, other_files do |add, const|
- if add
- add_constant const
- else
- @constants.delete const
- @constants_hash.delete const.name
+ array[6].each do |constant, document, file|
+ case constant
+ when Constant
+ add_constant constant
+ else
+ constant = add_constant Constant.new(constant, nil, Comment.from_document(document))
+ constant.record_location TopLevel.new file
+ end
end
- end
- merge_collections includes, cm.includes, other_files do |add, incl|
- if add
- add_include incl
- else
- @includes.delete incl
+ array[7].each do |name, document, file|
+ incl = add_include Include.new(name, Comment.from_document(document))
+ incl.record_location TopLevel.new file
end
- end
- @includes.uniq! # clean up
+ array[8].each do |type, visibilities|
+ visibilities.each do |visibility, methods|
+ methods.each do |name, file|
+ method = AnyMethod.new name, singleton: type == 'class'
+ method.record_location TopLevel.new file
+ method.visibility = visibility
+ add_method method
+ end
+ end
+ end
- merge_collections extends, cm.extends, other_files do |add, ext|
- if add
- add_extend ext
- else
- @extends.delete ext
+ array[9].each do |name, document, file|
+ ext = add_extend Extend.new(name, Comment.from_document(document))
+ ext.record_location TopLevel.new file
+ end if array[9] # Support Marshal version 1
+
+ sections = (array[10] || []).map do |section|
+ [section.title, section]
end
- end
- @extends.uniq! # clean up
+ @sections = Hash[*sections.flatten]
+ @current_section = add_section nil
- merge_collections method_list, cm.method_list, other_files do |add, meth|
- if add
- add_method meth
- else
- @method_list.delete meth
- @methods_hash.delete meth.pretty_name
+ @in_files = []
+
+ (array[11] || []).each do |filename|
+ record_location TopLevel.new filename
end
+
+ @parent_name = array[12]
+ @parent_class = array[13]
end
- merge_sections cm
+ ##
+ # Merges +class_module+ into this ClassModule.
+ #
+ # The data in +class_module+ is preferred over the receiver.
- self
- end
+ def merge(class_module)
+ @parent = class_module.parent
+ @parent_name = class_module.parent_name
- ##
- # Merges collection +mine+ with +other+ preferring other. +other_files+ is
- # used to help determine which items should be deleted.
- #
- # Yields whether the item should be added or removed (true or false) and the
- # item to be added or removed.
- #
- # merge_collections things, other.things, other.in_files do |add, thing|
- # if add
- # # add the thing
- # else
- # # remove the thing
- # end
- # end
-
- def merge_collections(mine, other, other_files, &block) # :nodoc:
- my_things = mine. group_by { |thing| thing.file }
- other_things = other.group_by { |thing| thing.file }
-
- remove_things my_things, other_files, &block
- add_things my_things, other_things, &block
- end
+ other_document = parse class_module.comment_location
- ##
- # Merges the comments in this ClassModule with the comments in the other
- # ClassModule +cm+.
+ if other_document
+ document = parse @comment_location
- def merge_sections(cm) # :nodoc:
- my_sections = sections.group_by { |section| section.title }
- other_sections = cm.sections.group_by { |section| section.title }
+ document = document.merge other_document
- other_files = cm.in_files
+ @comment = Comment.from_document(document)
- remove_things my_sections, other_files do |_, section|
- @sections.delete section.title
- end
+ @comment_location = if document.parts.first.is_a?(Markup::Document)
+ document.parts.group_by(&:file)
+ else
+ { document.file => [document] }
+ end
+ end
- other_sections.each do |group, sections|
- if my_sections.include? group
- my_sections[group].each do |my_section|
- other_section = cm.sections_hash[group]
+ cm = class_module
+ other_files = cm.in_files
- my_comments = my_section.comments
- other_comments = other_section.comments
+ merge_collections attributes, cm.attributes, other_files do |add, attr|
+ if add
+ add_attribute attr
+ else
+ @attributes.delete attr
+ @methods_hash.delete attr.pretty_name
+ end
+ end
- other_files = other_section.in_files
+ merge_collections constants, cm.constants, other_files do |add, const|
+ if add
+ add_constant const
+ else
+ @constants.delete const
+ @constants_hash.delete const.name
+ end
+ end
- merge_collections my_comments, other_comments, other_files do |add, comment|
- if add
- my_section.add_comment comment
- else
- my_section.remove_comment comment
- end
- end
+ merge_collections includes, cm.includes, other_files do |add, incl|
+ if add
+ add_include incl
+ else
+ @includes.delete incl
end
- else
- sections.each do |section|
- add_section group, section.comments
+ end
+
+ @includes.uniq! # clean up
+
+ merge_collections extends, cm.extends, other_files do |add, ext|
+ if add
+ add_extend ext
+ else
+ @extends.delete ext
end
end
- end
- end
- ##
- # Does this object represent a module?
+ @extends.uniq! # clean up
- def module?
- false
- end
+ merge_collections method_list, cm.method_list, other_files do |add, meth|
+ if add
+ add_method meth
+ else
+ @method_list.delete meth
+ @methods_hash.delete meth.pretty_name
+ end
+ end
- ##
- # Allows overriding the initial name.
- #
- # Used for modules and classes that are constant aliases.
+ merge_sections cm
- def name=(new_name)
- @name = new_name
- end
+ self
+ end
- ##
- # Parses +comment_location+ into an RDoc::Markup::Document composed of
- # multiple RDoc::Markup::Documents with their file set.
+ ##
+ # Merges collection +mine+ with +other+ preferring other. +other_files+ is
+ # used to help determine which items should be deleted.
+ #
+ # Yields whether the item should be added or removed (true or false) and the
+ # item to be added or removed.
+ #
+ # merge_collections things, other.things, other.in_files do |add, thing|
+ # if add
+ # # add the thing
+ # else
+ # # remove the thing
+ # end
+ # end
- def parse(comment_location)
- case comment_location
- when String
- super
- when Hash
- docs = comment_location.flat_map do |location, comments|
- comments.map do |comment|
- doc = super comment
- doc.file = location
- doc
- end
- end
+ def merge_collections(mine, other, other_files, &block) # :nodoc:
+ my_things = mine. group_by { |thing| thing.file }
+ other_things = other.group_by { |thing| thing.file }
- RDoc::Markup::Document.new(*docs)
- when RDoc::Comment
- doc = super comment_location.text, comment_location.format
- doc.file = comment_location.location
- doc
- when RDoc::Markup::Document
- return comment_location
- else
- raise ArgumentError, "unknown comment class #{comment_location.class}"
+ remove_things my_things, other_files, &block
+ add_things my_things, other_things, &block
end
- end
- ##
- # Path to this class or module for use with HTML generator output.
+ ##
+ # Merges the comments in this ClassModule with the comments in the other
+ # ClassModule +cm+.
- def path
- prefix = options.class_module_path_prefix
- return http_url unless prefix
- File.join(prefix, http_url)
- end
+ def merge_sections(cm) # :nodoc:
+ my_sections = sections.group_by { |section| section.title }
+ other_sections = cm.sections.group_by { |section| section.title }
- ##
- # Name to use to generate the url:
- # modules and classes that are aliases for another
- # module or class return the name of the latter.
+ other_files = cm.in_files
- def name_for_path
- is_alias_for ? is_alias_for.name_for_path : full_name
- end
+ remove_things my_sections, other_files do |_, section|
+ @sections.delete section.title
+ end
- ##
- # Returns the classes and modules that are not constants
- # aliasing another class or module. For use by formatters
- # only (caches its result).
+ other_sections.each do |group, sections|
+ if my_sections.include? group
+ my_sections[group].each do |my_section|
+ other_section = cm.sections_hash[group]
- def non_aliases
- @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for }
- end
+ my_comments = my_section.comments
+ other_comments = other_section.comments
- ##
- # Updates the child modules or classes of class/module +parent+ by
- # deleting the ones that have been removed from the documentation.
- #
- # +parent_hash+ is either parent.modules_hash or
- # parent.classes_hash and +all_hash+ is ::all_modules_hash or
- # ::all_classes_hash.
+ other_files = other_section.in_files
- def remove_nodoc_children
- prefix = self.full_name + '::'
+ merge_collections my_comments, other_comments, other_files do |add, comment|
+ if add
+ my_section.add_comment comment
+ else
+ my_section.remove_comment comment
+ end
+ end
+ end
+ else
+ sections.each do |section|
+ add_section group, section.comments
+ end
+ end
+ end
+ end
- modules_hash.each_key do |name|
- full_name = prefix + name
- modules_hash.delete name unless @store.modules_hash[full_name]
+ ##
+ # Does this object represent a module?
+
+ def module?
+ false
end
- classes_hash.each_key do |name|
- full_name = prefix + name
- classes_hash.delete name unless @store.classes_hash[full_name]
+ ##
+ # Allows overriding the initial name.
+ #
+ # Used for modules and classes that are constant aliases.
+
+ def name=(new_name)
+ @name = new_name
end
- end
- def remove_things(my_things, other_files) # :nodoc:
- my_things.delete_if do |file, things|
- next false unless other_files.include? file
+ ##
+ # Parses +comment_location+ into an RDoc::Markup::Document composed of
+ # multiple RDoc::Markup::Documents with their file set.
- things.each do |thing|
- yield false, thing
+ def parse(comment_location)
+ case comment_location
+ when String
+ super
+ when Hash
+ docs = comment_location.flat_map do |location, comments|
+ comments.map do |comment|
+ doc = super comment
+ doc.file = location
+ doc
+ end
+ end
+
+ Markup::Document.new(*docs)
+ when Comment
+ doc = super comment_location.text, comment_location.format
+ doc.file = comment_location.location
+ doc
+ when Markup::Document
+ return comment_location
+ else
+ raise ArgumentError, "unknown comment class #{comment_location.class}"
end
+ end
+
+ ##
+ # Path to this class or module for use with HTML generator output.
- true
+ def path
+ prefix = options.class_module_path_prefix
+ return http_url unless prefix
+ File.join(prefix, http_url)
end
- end
- ##
- # Search record used by RDoc::Generator::JsonIndex
- #
- # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
- # Use #search_snippet instead for getting documentation snippets.
-
- def search_record
- [
- name,
- full_name,
- full_name,
- '',
- path,
- '',
- snippet(@comment_location),
- ]
- end
+ ##
+ # Name to use to generate the url:
+ # modules and classes that are aliases for another
+ # module or class return the name of the latter.
- ##
- # Returns an HTML snippet of the first comment for search results.
+ def name_for_path
+ is_alias_for ? is_alias_for.name_for_path : full_name
+ end
- def search_snippet
- first_comment = @comment_location.each_value.first&.first
- return '' unless first_comment && !first_comment.empty?
+ ##
+ # Returns the classes and modules that are not constants
+ # aliasing another class or module. For use by formatters
+ # only (caches its result).
- snippet(first_comment)
- end
+ def non_aliases
+ @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for }
+ end
- ##
- # Rebuilds +@comment+ from the current +@comment_location+ entries,
- # skipping any empty placeholders.
-
- def rebuild_comment_from_location
- texts = @comment_location.each_value.flat_map { |comments|
- comments.filter_map { |c| c.to_s unless c.empty? }
- }
- merged = texts.join("\n---\n")
- @comment = merged.empty? ? '' : RDoc::Comment.new(merged)
- end
+ ##
+ # Updates the child modules or classes of class/module +parent+ by
+ # deleting the ones that have been removed from the documentation.
+ #
+ # +parent_hash+ is either parent.modules_hash or
+ # parent.classes_hash and +all_hash+ is ::all_modules_hash or
+ # ::all_classes_hash.
- ##
- # Sets the store for this class or module and its contained code objects.
+ def remove_nodoc_children
+ prefix = self.full_name + '::'
- def store=(store)
- super
+ modules_hash.each_key do |name|
+ full_name = prefix + name
+ modules_hash.delete name unless @store.modules_hash[full_name]
+ end
- @attributes .each do |attr| attr.store = store end
- @constants .each do |const| const.store = store end
- @includes .each do |incl| incl.store = store end
- @extends .each do |ext| ext.store = store end
- @method_list.each do |meth| meth.store = store end
- end
+ classes_hash.each_key do |name|
+ full_name = prefix + name
+ classes_hash.delete name unless @store.classes_hash[full_name]
+ end
+ end
- ##
- # Get the superclass of this class. Attempts to retrieve the superclass
- # object, returns the name if it is not known.
+ def remove_things(my_things, other_files) # :nodoc:
+ my_things.delete_if do |file, things|
+ next false unless other_files.include? file
- def superclass
- @store.find_class_named(@superclass) || @superclass
- end
+ things.each do |thing|
+ yield false, thing
+ end
- ##
- # Set the superclass of this class to +superclass+
- #
- # where +superclass+ is one of:
- #
- # - +nil+
- # - a String containing the full name of the superclass
- # - the RDoc::ClassModule representing the superclass
-
- def superclass=(superclass)
- raise NoMethodError, "#{full_name} is a module" if module?
- case superclass
- when RDoc::ClassModule
- @superclass = superclass.full_name
- when nil, String
- @superclass = superclass
- else
- raise TypeError, "superclass must be a String or RDoc::ClassModule, not #{superclass.class}"
+ true
+ end
end
- end
- ##
- # Get all super classes of this class in an array. The last element might be
- # a string if the name is unknown.
-
- def super_classes
- result = []
- # Degenerate input can produce a cyclic superclass chain
- visited = [full_name]
- parent = self
- while parent = parent.superclass
- if parent.is_a?(String)
- result << parent
- break
- end
- break if visited.include?(parent.full_name)
- visited << parent.full_name
- result << parent
+ ##
+ # Search record used by RDoc::Generator::JsonIndex
+ #
+ # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
+ # Use #search_snippet instead for getting documentation snippets.
+
+ def search_record
+ [
+ name,
+ full_name,
+ full_name,
+ '',
+ path,
+ '',
+ snippet(@comment_location),
+ ]
end
- result
- end
- def to_s # :nodoc:
- if is_alias_for
- "#{self.class.name} #{self.full_name} -> #{is_alias_for}"
- else
- super
+ ##
+ # Returns an HTML snippet of the first comment for search results.
+
+ def search_snippet
+ first_comment = @comment_location.each_value.first&.first
+ return '' unless first_comment && !first_comment.empty?
+
+ snippet(first_comment)
end
- end
- ##
- # 'module' or 'class'
+ ##
+ # Rebuilds +@comment+ from the current +@comment_location+ entries,
+ # skipping any empty placeholders.
- def type
- module? ? 'module' : 'class'
- end
+ def rebuild_comment_from_location
+ texts = @comment_location.each_value.flat_map { |comments|
+ comments.filter_map { |c| c.to_s unless c.empty? }
+ }
+ merged = texts.join("\n---\n")
+ @comment = merged.empty? ? '' : Comment.new(merged)
+ end
- ##
- # Updates the child modules & classes by replacing the ones that are
- # aliases through a constant.
- #
- # The aliased module/class is replaced in the children and in
- # RDoc::Store#modules_hash or RDoc::Store#classes_hash
- # by a copy that has RDoc::ClassModule#is_alias_for set to
- # the aliased module/class, and this copy is added to #aliases
- # of the aliased module/class.
- #
- # Formatters can use the #non_aliases method to retrieve children that
- # are not aliases, for instance to list the namespace content, since
- # the aliased modules are included in the constants of the class/module,
- # that are listed separately.
-
- def update_aliases
- constants.each do |const|
- cm = const.is_alias_for
- cm ||= const.resolved_alias_target if const.is_a?(RDoc::Constant)
- next unless cm
-
- # Resolve chained aliases (A = B = C) to the real class/module.
- cm = @store.find_class_or_module(cm.full_name) || cm
- while (target = cm.is_alias_for)
- cm = target
- end
-
- cm_alias = cm.dup
- cm_alias.name = const.name
-
- if full_name == 'Object'
- # Don't move top-level aliases under Object, they look ugly there
- cm_alias.parent = top_level
+ ##
+ # Sets the store for this class or module and its contained code objects.
+
+ def store=(store)
+ super
+
+ @attributes .each do |attr| attr.store = store end
+ @constants .each do |const| const.store = store end
+ @includes .each do |incl| incl.store = store end
+ @extends .each do |ext| ext.store = store end
+ @method_list.each do |meth| meth.store = store end
+ end
+
+ ##
+ # Get the superclass of this class. Attempts to retrieve the superclass
+ # object, returns the name if it is not known.
+
+ def superclass
+ @store.find_class_named(@superclass) || @superclass
+ end
+
+ ##
+ # Set the superclass of this class to +superclass+
+ #
+ # where +superclass+ is one of:
+ #
+ # - +nil+
+ # - a String containing the full name of the superclass
+ # - the RDoc::ClassModule representing the superclass
+
+ def superclass=(superclass)
+ raise NoMethodError, "#{full_name} is a module" if module?
+ case superclass
+ when ClassModule
+ @superclass = superclass.full_name
+ when nil, String
+ @superclass = superclass
else
- cm_alias.parent = self
+ raise TypeError, "superclass must be a String or RDoc::ClassModule, not #{superclass.class}"
end
- cm_alias.full_name = nil # force update for new parent
-
- # Don't clobber a real (non-alias) class/module already living at this
- # name. Mirrors the BasicObject = BlankSlate guard in
- # Context#add_module_alias. Existing alias copies (set by
- # add_module_alias or a previous update_aliases pass) carry is_alias_for,
- # so they're still overwritable here.
- existing = @store.find_class_or_module(cm_alias.full_name)
- next if existing && !existing.is_alias_for
+ end
- # Persist a lazy-resolved target so Stats#report_constants and
- # Constant#marshal_dump observe the alias relationship. Skipped
- # aliases (above) intentionally leave the constant unmarked.
- const.is_alias_for ||= cm
+ ##
+ # Get all super classes of this class in an array. The last element might be
+ # a string if the name is unknown.
- cm_alias.aliases.clear
- cm_alias.is_alias_for = cm
+ def super_classes
+ result = []
+ # Degenerate input can produce a cyclic superclass chain
+ visited = [full_name]
+ parent = self
+ while parent = parent.superclass
+ if parent.is_a?(String)
+ result << parent
+ break
+ end
+ break if visited.include?(parent.full_name)
+ visited << parent.full_name
+ result << parent
+ end
+ result
+ end
- if cm.module?
- @store.modules_hash[cm_alias.full_name] = cm_alias
- modules_hash[const.name] = cm_alias
+ def to_s # :nodoc:
+ if is_alias_for
+ "#{self.class.name} #{self.full_name} -> #{is_alias_for}"
else
- @store.classes_hash[cm_alias.full_name] = cm_alias
- classes_hash[const.name] = cm_alias
+ super
end
-
- cm.aliases << cm_alias
end
- end
- ##
- # Deletes from #includes those whose module has been removed from the
- # documentation.
- #--
- # FIXME: includes are not reliably removed, see _possible_bug test case
+ ##
+ # 'module' or 'class'
+
+ def type
+ module? ? 'module' : 'class'
+ end
+
+ ##
+ # Updates the child modules & classes by replacing the ones that are
+ # aliases through a constant.
+ #
+ # The aliased module/class is replaced in the children and in
+ # RDoc::Store#modules_hash or RDoc::Store#classes_hash
+ # by a copy that has RDoc::ClassModule#is_alias_for set to
+ # the aliased module/class, and this copy is added to #aliases
+ # of the aliased module/class.
+ #
+ # Formatters can use the #non_aliases method to retrieve children that
+ # are not aliases, for instance to list the namespace content, since
+ # the aliased modules are included in the constants of the class/module,
+ # that are listed separately.
+
+ def update_aliases
+ constants.each do |const|
+ cm = const.is_alias_for
+ cm ||= const.resolved_alias_target if const.is_a?(Constant)
+ next unless cm
+
+ # Resolve chained aliases (A = B = C) to the real class/module.
+ cm = @store.find_class_or_module(cm.full_name) || cm
+ while (target = cm.is_alias_for)
+ cm = target
+ end
- def update_includes
- includes.reject! do |include|
- mod = include.module
- !(String === mod) && @store.modules_hash[mod.full_name].nil?
- end
+ cm_alias = cm.dup
+ cm_alias.name = const.name
- includes.uniq!
- end
+ if full_name == 'Object'
+ # Don't move top-level aliases under Object, they look ugly there
+ cm_alias.parent = top_level
+ else
+ cm_alias.parent = self
+ end
+ cm_alias.full_name = nil # force update for new parent
+
+ # Don't clobber a real (non-alias) class/module already living at this
+ # name. Mirrors the BasicObject = BlankSlate guard in
+ # Context#add_module_alias. Existing alias copies (set by
+ # add_module_alias or a previous update_aliases pass) carry is_alias_for,
+ # so they're still overwritable here.
+ existing = @store.find_class_or_module(cm_alias.full_name)
+ next if existing && !existing.is_alias_for
+
+ # Persist a lazy-resolved target so Stats#report_constants and
+ # Constant#marshal_dump observe the alias relationship. Skipped
+ # aliases (above) intentionally leave the constant unmarked.
+ const.is_alias_for ||= cm
+
+ cm_alias.aliases.clear
+ cm_alias.is_alias_for = cm
+
+ if cm.module?
+ @store.modules_hash[cm_alias.full_name] = cm_alias
+ modules_hash[const.name] = cm_alias
+ else
+ @store.classes_hash[cm_alias.full_name] = cm_alias
+ classes_hash[const.name] = cm_alias
+ end
- ##
- # Deletes from #extends those whose module has been removed from the
- # documentation.
- #--
- # FIXME: like update_includes, extends are not reliably removed
+ cm.aliases << cm_alias
+ end
+ end
+
+ ##
+ # Deletes from #includes those whose module has been removed from the
+ # documentation.
+ #--
+ # FIXME: includes are not reliably removed, see _possible_bug test case
- def update_extends
- extends.reject! do |ext|
- mod = ext.module
+ def update_includes
+ includes.reject! do |include|
+ mod = include.module
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
+ end
- !(String === mod) && @store.modules_hash[mod.full_name].nil?
+ includes.uniq!
end
- extends.uniq!
- end
+ ##
+ # Deletes from #extends those whose module has been removed from the
+ # documentation.
+ #--
+ # FIXME: like update_includes, extends are not reliably removed
- def embed_mixins
- return unless options.embed_mixins
+ def update_extends
+ extends.reject! do |ext|
+ mod = ext.module
- includes.each do |include|
- next if String === include.module
- include.module.method_list.each do |code_object|
- add_method(prepare_to_embed(code_object))
- end
- include.module.constants.each do |code_object|
- add_constant(prepare_to_embed(code_object))
- end
- include.module.attributes.each do |code_object|
- add_attribute(prepare_to_embed(code_object))
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
end
+
+ extends.uniq!
end
- extends.each do |ext|
- next if String === ext.module
- ext.module.method_list.each do |code_object|
- add_method(prepare_to_embed(code_object, true))
+ def embed_mixins
+ return unless options.embed_mixins
+
+ includes.each do |include|
+ next if String === include.module
+ include.module.method_list.each do |code_object|
+ add_method(prepare_to_embed(code_object))
+ end
+ include.module.constants.each do |code_object|
+ add_constant(prepare_to_embed(code_object))
+ end
+ include.module.attributes.each do |code_object|
+ add_attribute(prepare_to_embed(code_object))
+ end
end
- ext.module.attributes.each do |code_object|
- add_attribute(prepare_to_embed(code_object, true))
+
+ extends.each do |ext|
+ next if String === ext.module
+ ext.module.method_list.each do |code_object|
+ add_method(prepare_to_embed(code_object, true))
+ end
+ ext.module.attributes.each do |code_object|
+ add_attribute(prepare_to_embed(code_object, true))
+ end
end
end
- end
private
- def prepare_to_embed(code_object, singleton=false)
- code_object = code_object.dup
- code_object.mixin_from = code_object.parent
- code_object.singleton = true if singleton
- set_current_section(code_object.section.title, code_object.section.comment)
- code_object
+ def prepare_to_embed(code_object, singleton=false)
+ code_object = code_object.dup
+ code_object.mixin_from = code_object.parent
+ code_object.singleton = true if singleton
+ set_current_section(code_object.section.title, code_object.section.comment)
+ code_object
+ end
end
end
diff --git a/lib/rdoc/code_object/constant.rb b/lib/rdoc/code_object/constant.rb
index 7fe34f6d0d..c517d4b2f9 100644
--- a/lib/rdoc/code_object/constant.rb
+++ b/lib/rdoc/code_object/constant.rb
@@ -1,221 +1,223 @@
# frozen_string_literal: true
-##
-# A constant
+module RDoc
+ ##
+ # A constant
-class RDoc::Constant < RDoc::CodeObject
+ class Constant < CodeObject
- MARSHAL_VERSION = 0 # :nodoc:
+ MARSHAL_VERSION = 0 # :nodoc:
- ##
- # Sets the module or class this is constant is an alias for.
+ ##
+ # Sets the module or class this is constant is an alias for.
- attr_writer :is_alias_for
+ attr_writer :is_alias_for
- ##
- # The constant's name
+ ##
+ # The constant's name
- attr_accessor :name
+ attr_accessor :name
- ##
- # The constant's value
+ ##
+ # The constant's value
- attr_accessor :value
+ attr_accessor :value
- ##
- # The constant's visibility
+ ##
+ # The constant's visibility
- attr_accessor :visibility
+ attr_accessor :visibility
- ##
- # The constant path on the RHS when the RHS is a bare constant reference
- # (+Foo = Bar+ or +Foo = Bar::Baz+). Captured at parse time so
- # #resolved_alias_target doesn't have to re-derive it from the textual
- # #value. nil for other RHS shapes.
+ ##
+ # The constant path on the RHS when the RHS is a bare constant reference
+ # (+Foo = Bar+ or +Foo = Bar::Baz+). Captured at parse time so
+ # #resolved_alias_target doesn't have to re-derive it from the textual
+ # #value. nil for other RHS shapes.
- attr_accessor :is_alias_for_path
+ attr_accessor :is_alias_for_path
- ##
- # Creates a new constant with +name+, +value+ and +comment+
+ ##
+ # Creates a new constant with +name+, +value+ and +comment+
- def initialize(name, value, comment)
- super()
+ def initialize(name, value, comment)
+ super()
- @name = name
- @value = value
+ @name = name
+ @value = value
- @is_alias_for = nil
- @is_alias_for_path = nil
- @visibility = :public
+ @is_alias_for = nil
+ @is_alias_for_path = nil
+ @visibility = :public
- self.comment = comment
- end
+ self.comment = comment
+ end
- ##
- # Constants are ordered by name
+ ##
+ # Constants are ordered by name
- def <=>(other)
- return unless self.class === other
+ def <=>(other)
+ return unless self.class === other
- [parent_name, name] <=> [other.parent_name, other.name]
- end
+ [parent_name, name] <=> [other.parent_name, other.name]
+ end
- ##
- # Constants are equal when their #parent and #name is the same
+ ##
+ # Constants are equal when their #parent and #name is the same
- def ==(other)
- self.class == other.class and
- @parent == other.parent and
- @name == other.name
- end
+ def ==(other)
+ self.class == other.class and
+ @parent == other.parent and
+ @name == other.name
+ end
- ##
- # A constant is documented if it has a comment, or is an alias
- # for a documented class or module.
-
- def documented?
- return true if super
- return false unless @is_alias_for
- case @is_alias_for
- when String
- found = @store.find_class_or_module @is_alias_for
- return false unless found
- @is_alias_for = found
+ ##
+ # A constant is documented if it has a comment, or is an alias
+ # for a documented class or module.
+
+ def documented?
+ return true if super
+ return false unless @is_alias_for
+ case @is_alias_for
+ when String
+ found = @store.find_class_or_module @is_alias_for
+ return false unless found
+ @is_alias_for = found
+ end
+ @is_alias_for.documented?
end
- @is_alias_for.documented?
- end
- ##
- # Full constant name including namespace
+ ##
+ # Full constant name including namespace
- def full_name
- @full_name ||= "#{parent_name}::#{@name}"
- end
+ def full_name
+ @full_name ||= "#{parent_name}::#{@name}"
+ end
- ##
- # The module or class this constant is an alias for, when one was recorded
- # explicitly (by RDoc::Context#add_module_alias, RDoc::ClassModule#update_aliases,
- # or ri marshal load). Pure accessor; see #resolved_alias_target for the
- # opportunistic lookup path.
-
- def is_alias_for
- case @is_alias_for
- when String
- found = @store.find_class_or_module @is_alias_for
- @is_alias_for = found if found
- @is_alias_for
- else
- @is_alias_for
+ ##
+ # The module or class this constant is an alias for, when one was recorded
+ # explicitly (by RDoc::Context#add_module_alias, RDoc::ClassModule#update_aliases,
+ # or ri marshal load). Pure accessor; see #resolved_alias_target for the
+ # opportunistic lookup path.
+
+ def is_alias_for
+ case @is_alias_for
+ when String
+ found = @store.find_class_or_module @is_alias_for
+ @is_alias_for = found if found
+ @is_alias_for
+ else
+ @is_alias_for
+ end
end
- end
- ##
- # Returns the class/module this constant *would* alias if #is_alias_for_path
- # was set by the parser and that path resolves to a known class/module, or
- # nil. Used to support `Const = RHS` parsed before `class RHS;end` is defined
- # in another file. Pure lookup; does not mutate state. Honors :nodoc:
- # (returns nil if document_self is false). Note that module nesting
- # information is lost, so constant lookup is inaccurate.
-
- def resolved_alias_target
- return nil unless document_self
- return nil unless @is_alias_for_path
- parent.find_module_named(@is_alias_for_path)
- end
+ ##
+ # Returns the class/module this constant *would* alias if #is_alias_for_path
+ # was set by the parser and that path resolves to a known class/module, or
+ # nil. Used to support `Const = RHS` parsed before `class RHS;end` is defined
+ # in another file. Pure lookup; does not mutate state. Honors :nodoc:
+ # (returns nil if document_self is false). Note that module nesting
+ # information is lost, so constant lookup is inaccurate.
+
+ def resolved_alias_target
+ return nil unless document_self
+ return nil unless @is_alias_for_path
+ parent.find_module_named(@is_alias_for_path)
+ end
- def inspect # :nodoc:
- "#<%s:0x%x %s::%s>" % [
- self.class, object_id,
- parent_name, @name,
- ]
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x %s::%s>" % [
+ self.class, object_id,
+ parent_name, @name,
+ ]
+ end
- ##
- # Dumps this Constant for use by ri. See also #marshal_load
-
- def marshal_dump
- alias_name = case found = is_alias_for
- when RDoc::CodeObject then found.full_name
- else found
- end
-
- [ MARSHAL_VERSION,
- @name,
- full_name,
- @visibility,
- alias_name,
- parse(@comment),
- @file.relative_name,
- parent.name,
- parent.class,
- section.title,
- ]
- end
+ ##
+ # Dumps this Constant for use by ri. See also #marshal_load
+
+ def marshal_dump
+ alias_name = case found = is_alias_for
+ when CodeObject then found.full_name
+ else found
+ end
+
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @visibility,
+ alias_name,
+ parse(@comment),
+ @file.relative_name,
+ parent.name,
+ parent.class,
+ section.title,
+ ]
+ end
- ##
- # Loads this Constant from +array+. For a loaded Constant the following
- # methods will return cached values:
- #
- # * #full_name
- # * #parent_name
-
- def marshal_load(array)
- initialize array[1], nil, RDoc::Comment.from_document(array[5])
-
- @full_name = array[2]
- @visibility = array[3] || :public
- @is_alias_for = array[4]
- # 5 handled above
- # 6 handled below
- @parent_name = array[7]
- @parent_class = array[8]
- @section_title = array[9]
-
- @file = RDoc::TopLevel.new array[6]
- end
+ ##
+ # Loads this Constant from +array+. For a loaded Constant the following
+ # methods will return cached values:
+ #
+ # * #full_name
+ # * #parent_name
+
+ def marshal_load(array)
+ initialize array[1], nil, Comment.from_document(array[5])
+
+ @full_name = array[2]
+ @visibility = array[3] || :public
+ @is_alias_for = array[4]
+ # 5 handled above
+ # 6 handled below
+ @parent_name = array[7]
+ @parent_class = array[8]
+ @section_title = array[9]
+
+ @file = TopLevel.new array[6]
+ end
- ##
- # Path to this constant for use with HTML generator output.
+ ##
+ # Path to this constant for use with HTML generator output.
- def path
- "#{@parent.path}##{@name}"
- end
+ def path
+ "#{@parent.path}##{@name}"
+ end
- ##
- # Returns an HTML snippet of the comment for search results.
+ ##
+ # Returns an HTML snippet of the comment for search results.
- def search_snippet
- return '' if comment.empty?
+ def search_snippet
+ return '' if comment.empty?
- snippet(comment)
- end
+ snippet(comment)
+ end
- def pretty_print(q) # :nodoc:
- q.group 2, "[#{self.class.name} #{full_name}", "]" do
- unless comment.empty?
- q.breakable
- q.text "comment:"
- q.breakable
- q.pp @comment
+ def pretty_print(q) # :nodoc:
+ q.group 2, "[#{self.class.name} #{full_name}", "]" do
+ unless comment.empty?
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
end
end
- end
- ##
- # Sets the store for this class or module and its contained code objects.
+ ##
+ # Sets the store for this class or module and its contained code objects.
- def store=(store)
- super
+ def store=(store)
+ super
- @file = @store.add_file @file.full_name if @file
- end
+ @file = @store.add_file @file.full_name if @file
+ end
- def to_s # :nodoc:
- parent_name = parent ? parent.full_name : '(unknown)'
- if is_alias_for
- "constant #{parent_name}::#@name -> #{is_alias_for}"
- else
- "constant #{parent_name}::#@name"
+ def to_s # :nodoc:
+ parent_name = parent ? parent.full_name : '(unknown)'
+ if is_alias_for
+ "constant #{parent_name}::#@name -> #{is_alias_for}"
+ else
+ "constant #{parent_name}::#@name"
+ end
end
- end
+ end
end
diff --git a/lib/rdoc/code_object/context.rb b/lib/rdoc/code_object/context.rb
index ccc9203141..4f28d62600 100644
--- a/lib/rdoc/code_object/context.rb
+++ b/lib/rdoc/code_object/context.rb
@@ -1,1206 +1,1206 @@
# frozen_string_literal: true
-##
-# A Context is something that can hold modules, classes, methods, attributes,
-# aliases, requires, and includes. Classes, modules, and files are all
-# Contexts.
-
-class RDoc::Context < RDoc::CodeObject
-
- include Comparable
-
+module RDoc
##
- # Types of methods
+ # A Context is something that can hold modules, classes, methods, attributes,
+ # aliases, requires, and includes. Classes, modules, and files are all
+ # Contexts.
- TYPES = %w[class instance]
+ class Context < CodeObject
- ##
- # If a context has these titles it will be sorted in this order.
+ include Comparable
- TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
- TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
+ ##
+ # Types of methods
- ##
- # Class/module aliases
+ TYPES = %w[class instance]
- attr_reader :aliases
+ ##
+ # If a context has these titles it will be sorted in this order.
- ##
- # All attr* methods
+ TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
+ TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
- attr_reader :attributes
+ ##
+ # Class/module aliases
- ##
- # Block params to be used in the next MethodAttr parsed under this context
+ attr_reader :aliases
- attr_accessor :block_params
+ ##
+ # All attr* methods
- ##
- # Constants defined
+ attr_reader :attributes
- attr_reader :constants
+ ##
+ # Block params to be used in the next MethodAttr parsed under this context
- ##
- # Sets the current documentation section of documentation
+ attr_accessor :block_params
- attr_writer :current_section
+ ##
+ # Constants defined
- ##
- # Files this context is found in
+ attr_reader :constants
- attr_reader :in_files
+ ##
+ # Sets the current documentation section of documentation
- ##
- # Modules this context includes
+ attr_writer :current_section
- attr_reader :includes
+ ##
+ # Files this context is found in
- ##
- # Modules this context is extended with
+ attr_reader :in_files
- attr_reader :extends
+ ##
+ # Modules this context includes
- ##
- # Methods defined in this context
+ attr_reader :includes
- attr_reader :method_list
+ ##
+ # Modules this context is extended with
- ##
- # Name of this class excluding namespace. See also full_name
+ attr_reader :extends
- attr_reader :name
+ ##
+ # Methods defined in this context
- ##
- # Files this context requires
+ attr_reader :method_list
- attr_reader :requires
+ ##
+ # Name of this class excluding namespace. See also full_name
- ##
- # Use this section for the next method, attribute or constant added.
+ attr_reader :name
- attr_accessor :temporary_section
+ ##
+ # Files this context requires
- ##
- # Hash old_name => [aliases], for aliases
- # that haven't (yet) been resolved to a method/attribute.
- # (Not to be confused with the aliases of the context.)
+ attr_reader :requires
- attr_accessor :unmatched_alias_lists
+ ##
+ # Use this section for the next method, attribute or constant added.
- ##
- # Aliases that could not be resolved.
+ attr_accessor :temporary_section
- attr_reader :external_aliases
+ ##
+ # Hash old_name => [aliases], for aliases
+ # that haven't (yet) been resolved to a method/attribute.
+ # (Not to be confused with the aliases of the context.)
- ##
- # Hash of registered methods. Attributes are also registered here,
- # twice if they are RW.
+ attr_accessor :unmatched_alias_lists
- attr_reader :methods_hash
+ ##
+ # Aliases that could not be resolved.
- ##
- # Params to be used in the next MethodAttr parsed under this context
+ attr_reader :external_aliases
- attr_accessor :params
+ ##
+ # Hash of registered methods. Attributes are also registered here,
+ # twice if they are RW.
- ##
- # Hash of registered constants.
+ attr_reader :methods_hash
- attr_reader :constants_hash
+ ##
+ # Params to be used in the next MethodAttr parsed under this context
- ##
- # Creates an unnamed empty context
+ attr_accessor :params
- def initialize
- super
+ ##
+ # Hash of registered constants.
- @in_files = []
+ attr_reader :constants_hash
- @name ||= "unknown"
- @parent = nil
+ ##
+ # Creates an unnamed empty context
- @current_section = Section.new self, nil, nil
- @sections = { nil => @current_section }
- @temporary_section = nil
+ def initialize
+ super
- @classes = {}
- @modules = {}
+ @in_files = []
- initialize_methods_etc
- end
+ @name ||= "unknown"
+ @parent = nil
+ @current_section = Section.new self, nil, nil
+ @sections = { nil => @current_section }
+ @temporary_section = nil
- ##
- # Sets the defaults for methods and so-forth
+ @classes = {}
+ @modules = {}
- def initialize_methods_etc
- @method_list = []
- @attributes = []
- @aliases = []
- @requires = []
- @includes = []
- @extends = []
- @constants = []
- @external_aliases = []
+ initialize_methods_etc
+ end
- # This Hash maps a method name to a list of unmatched aliases (aliases of
- # a method not yet encountered).
- @unmatched_alias_lists = {}
+ ##
+ # Sets the defaults for methods and so-forth
- @methods_hash = {}
- @constants_hash = {}
+ def initialize_methods_etc
+ @method_list = []
+ @attributes = []
+ @aliases = []
+ @requires = []
+ @includes = []
+ @extends = []
+ @constants = []
+ @external_aliases = []
+ # This Hash maps a method name to a list of unmatched aliases (aliases of
+ # a method not yet encountered).
+ @unmatched_alias_lists = {}
- @params = nil
+ @methods_hash = {}
+ @constants_hash = {}
- @store ||= nil
- end
+ @params = nil
- ##
- # Contexts are sorted by full_name
+ @store ||= nil
+ end
- def <=>(other)
- return nil unless RDoc::CodeObject === other
+ ##
+ # Contexts are sorted by full_name
- full_name <=> other.full_name
- end
+ def <=>(other)
+ return nil unless CodeObject === other
- ##
- # Adds an item of type +klass+ with the given +name+ and +comment+ to the
- # context.
- #
- # Currently only RDoc::Extend and RDoc::Include are supported.
-
- def add(klass, name, comment)
- if RDoc::Extend == klass
- ext = RDoc::Extend.new name, comment
- add_extend ext
- elsif RDoc::Include == klass
- incl = RDoc::Include.new name, comment
- add_include incl
- else
- raise NotImplementedError, "adding a #{klass} is not implemented"
+ full_name <=> other.full_name
end
- end
- ##
- # Adds +an_alias+ that is automatically resolved
+ ##
+ # Adds an item of type +klass+ with the given +name+ and +comment+ to the
+ # context.
+ #
+ # Currently only RDoc::Extend and RDoc::Include are supported.
+
+ def add(klass, name, comment)
+ if Extend == klass
+ ext = Extend.new name, comment
+ add_extend ext
+ elsif Include == klass
+ incl = Include.new name, comment
+ add_include incl
+ else
+ raise NotImplementedError, "adding a #{klass} is not implemented"
+ end
+ end
- def add_alias(an_alias)
- return an_alias unless @document_self
+ ##
+ # Adds +an_alias+ that is automatically resolved
- method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
- find_attribute(an_alias.old_name, an_alias.singleton)
+ def add_alias(an_alias)
+ return an_alias unless @document_self
- if method_attr
- method_attr.add_alias an_alias, self
- else
- add_to @external_aliases, an_alias
- unmatched_alias_list =
- @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
- unmatched_alias_list.push an_alias
- end
+ method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
+ find_attribute(an_alias.old_name, an_alias.singleton)
- an_alias
- end
+ if method_attr
+ method_attr.add_alias an_alias, self
+ else
+ add_to @external_aliases, an_alias
+ unmatched_alias_list =
+ @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
+ unmatched_alias_list.push an_alias
+ end
- ##
- # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
- # updates the comment if it was empty.
- #
- # The attribute is registered only if it defines a new method.
- # For instance, attr_reader :foo will not be registered
- # if method +foo+ exists, but attr_accessor :foo will be registered
- # if method +foo+ exists, but foo= does not.
+ an_alias
+ end
- def add_attribute(attribute)
- return attribute unless @document_self
+ ##
+ # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
+ # updates the comment if it was empty.
+ #
+ # The attribute is registered only if it defines a new method.
+ # For instance, attr_reader :foo will not be registered
+ # if method +foo+ exists, but attr_accessor :foo will be registered
+ # if method +foo+ exists, but foo= does not.
- # mainly to check for redefinition of an attribute as a method
- # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
- register = false
+ def add_attribute(attribute)
+ return attribute unless @document_self
- key = nil
+ # mainly to check for redefinition of an attribute as a method
+ # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
+ register = false
- if attribute.rw.index 'R'
- key = attribute.pretty_name
- known = @methods_hash[key]
+ key = nil
- if known
- known.comment = attribute.comment if known.comment.empty?
- elsif registered = @methods_hash[attribute.pretty_name + '='] and
- RDoc::Attr === registered
- registered.rw = 'RW'
- else
- @methods_hash[key] = attribute
- register = true
+ if attribute.rw.index 'R'
+ key = attribute.pretty_name
+ known = @methods_hash[key]
+
+ if known
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name + '='] and
+ Attr === registered
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
end
- end
- if attribute.rw.index 'W'
- key = attribute.pretty_name + '='
- known = @methods_hash[key]
+ if attribute.rw.index 'W'
+ key = attribute.pretty_name + '='
+ known = @methods_hash[key]
- if known
- known.comment = attribute.comment if known.comment.empty?
- elsif registered = @methods_hash[attribute.pretty_name] and
- RDoc::Attr === registered
- registered.rw = 'RW'
- else
- @methods_hash[key] = attribute
- register = true
+ if known
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name] and
+ Attr === registered
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
end
- end
- if register
- add_to @attributes, attribute
- resolve_aliases attribute
- end
+ if register
+ add_to @attributes, attribute
+ resolve_aliases attribute
+ end
- attribute
- end
+ attribute
+ end
- ##
- # Adds a class named +given_name+ with +superclass+.
- #
- # Both +given_name+ and +superclass+ may contain '::', and are
- # interpreted relative to the +self+ context. This allows handling correctly
- # examples like these:
- # class RDoc::Gauntlet < Gauntlet
- # module Mod
- # class Object # implies < ::Object
- # class SubObject < Object # this is _not_ ::Object
- #
- # Given class Container::Item RDoc assumes +Container+ is a module
- # unless it later sees class Container. +add_class+ automatically
- # upgrades +given_name+ to a class in this case.
-
- def add_class(class_type, given_name, superclass = '::Object')
- # superclass +nil+ is passed by the C parser in the following cases:
- # - registering Object in 1.8 (correct)
- # - registering BasicObject in 1.9 (correct)
- # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
+ ##
+ # Adds a class named +given_name+ with +superclass+.
#
- # If we later find a superclass for a registered class with a nil
- # superclass, we must honor it.
-
- # find the name & enclosing context
- if given_name =~ /^:+(\w+)$/
- full_name = $1
- enclosing = top_level
- name = full_name.split(/:+/).last
- else
- full_name = child_name given_name
-
- if full_name =~ /^(.+)::(\w+)$/
- name = $2
- ename = $1
- enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
- # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
- unless enclosing
- # try the given name at top level (will work for the above example)
- enclosing = @store.classes_hash[given_name] ||
- @store.modules_hash[given_name]
- return enclosing if enclosing
- # not found: create the parent(s)
- enclosing = find_or_create_namespace_path ename
- end
+ # Both +given_name+ and +superclass+ may contain '::', and are
+ # interpreted relative to the +self+ context. This allows handling correctly
+ # examples like these:
+ # class RDoc::Gauntlet < Gauntlet
+ # module Mod
+ # class Object # implies < ::Object
+ # class SubObject < Object # this is _not_ ::Object
+ #
+ # Given class Container::Item RDoc assumes +Container+ is a module
+ # unless it later sees class Container. +add_class+ automatically
+ # upgrades +given_name+ to a class in this case.
+
+ def add_class(class_type, given_name, superclass = '::Object')
+ # superclass +nil+ is passed by the C parser in the following cases:
+ # - registering Object in 1.8 (correct)
+ # - registering BasicObject in 1.9 (correct)
+ # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
+ #
+ # If we later find a superclass for a registered class with a nil
+ # superclass, we must honor it.
+
+ # find the name & enclosing context
+ if given_name =~ /^:+(\w+)$/
+ full_name = $1
+ enclosing = top_level
+ name = full_name.split(/:+/).last
else
- name = full_name
- enclosing = self
+ full_name = child_name given_name
+
+ if full_name =~ /^(.+)::(\w+)$/
+ name = $2
+ ename = $1
+ enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
+ # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
+ unless enclosing
+ # try the given name at top level (will work for the above example)
+ enclosing = @store.classes_hash[given_name] ||
+ @store.modules_hash[given_name]
+ return enclosing if enclosing
+ # not found: create the parent(s)
+ enclosing = find_or_create_namespace_path ename
+ end
+ else
+ name = full_name
+ enclosing = self
+ end
end
- end
- # fix up superclass
- if full_name == 'BasicObject'
- superclass = nil
- elsif full_name == 'Object'
- superclass = '::BasicObject'
- end
+ # fix up superclass
+ if full_name == 'BasicObject'
+ superclass = nil
+ elsif full_name == 'Object'
+ superclass = '::BasicObject'
+ end
- # find the superclass full name
- if superclass
- if superclass =~ /^:+/
- superclass = $' #'
- else
- if superclass =~ /^(\w+):+(.+)$/
- suffix = $2
- mod = find_module_named($1)
- superclass = mod.full_name + '::' + suffix if mod
+ # find the superclass full name
+ if superclass
+ if superclass =~ /^:+/
+ superclass = $' #'
else
- mod = find_module_named(superclass)
- superclass = mod.full_name if mod
+ if superclass =~ /^(\w+):+(.+)$/
+ suffix = $2
+ mod = find_module_named($1)
+ superclass = mod.full_name + '::' + suffix if mod
+ else
+ mod = find_module_named(superclass)
+ superclass = mod.full_name if mod
+ end
end
+
+ # did we believe it was a module?
+ mod = @store.modules_hash.delete superclass
+
+ upgrade_to_class mod, NormalClass, mod.parent if mod
+
+ # e.g., Object < Object
+ superclass = nil if superclass == full_name
end
- # did we believe it was a module?
- mod = @store.modules_hash.delete superclass
+ klass = @store.classes_hash[full_name]
- upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
+ if klass
+ # if TopLevel, it may not be registered in the classes:
+ enclosing.classes_hash[name] = klass
- # e.g., Object < Object
- superclass = nil if superclass == full_name
- end
+ # update the superclass if needed
+ if superclass
+ existing = klass.superclass
+ existing = existing.full_name unless existing.is_a?(String) if existing
+ if existing.nil? ||
+ (existing == 'Object' && superclass != 'Object')
+ klass.superclass = superclass
+ end
+ end
+ else
+ # this is a new class
+ mod = @store.modules_hash.delete full_name
- klass = @store.classes_hash[full_name]
+ if mod
+ klass = upgrade_to_class mod, NormalClass, enclosing
- if klass
- # if TopLevel, it may not be registered in the classes:
- enclosing.classes_hash[name] = klass
+ klass.superclass = superclass unless superclass.nil?
+ else
+ klass = class_type.new name, superclass
- # update the superclass if needed
- if superclass
- existing = klass.superclass
- existing = existing.full_name unless existing.is_a?(String) if existing
- if existing.nil? ||
- (existing == 'Object' && superclass != 'Object')
- klass.superclass = superclass
+ enclosing.add_class_or_module(klass, enclosing.classes_hash,
+ @store.classes_hash)
end
end
- else
- # this is a new class
- mod = @store.modules_hash.delete full_name
- if mod
- klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
+ klass.parent = self
- klass.superclass = superclass unless superclass.nil?
- else
- klass = class_type.new name, superclass
+ klass
+ end
- enclosing.add_class_or_module(klass, enclosing.classes_hash,
- @store.classes_hash)
+ ##
+ # Adds the class or module +mod+ to the modules or
+ # classes Hash +self_hash+, and to +all_hash+ (either
+ # TopLevel::modules_hash or TopLevel::classes_hash),
+ # unless #done_documenting is +true+. Sets the #parent of +mod+
+ # to +self+, and its #section to #current_section. Returns +mod+.
+
+ def add_class_or_module(mod, self_hash, all_hash)
+ mod.section = current_section # TODO declaring context? something is
+ # wrong here...
+ mod.parent = self
+ mod.full_name = nil
+ mod.store = @store
+
+ unless @done_documenting
+ self_hash[mod.name] = mod
+ # this must be done AFTER adding mod to its parent, so that the full
+ # name is correct:
+ all_hash[mod.full_name] = mod
end
+
+ mod
end
- klass.parent = self
+ ##
+ # Adds +constant+ if not already there. If it is, updates the comment,
+ # value and/or is_alias_for of the known constant if they were empty/nil.
- klass
- end
+ def add_constant(constant)
+ return constant unless @document_self
- ##
- # Adds the class or module +mod+ to the modules or
- # classes Hash +self_hash+, and to +all_hash+ (either
- # TopLevel::modules_hash or TopLevel::classes_hash),
- # unless #done_documenting is +true+. Sets the #parent of +mod+
- # to +self+, and its #section to #current_section. Returns +mod+.
-
- def add_class_or_module(mod, self_hash, all_hash)
- mod.section = current_section # TODO declaring context? something is
- # wrong here...
- mod.parent = self
- mod.full_name = nil
- mod.store = @store
-
- unless @done_documenting
- self_hash[mod.name] = mod
- # this must be done AFTER adding mod to its parent, so that the full
- # name is correct:
- all_hash[mod.full_name] = mod
- end
-
- mod
- end
+ # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
+ # (this is a #ifdef: should be handled by the C parser)
+ known = @constants_hash[constant.name]
- ##
- # Adds +constant+ if not already there. If it is, updates the comment,
- # value and/or is_alias_for of the known constant if they were empty/nil.
+ if known
+ known.comment = constant.comment if known.comment.empty?
- def add_constant(constant)
- return constant unless @document_self
+ known.value = constant.value if
+ known.value.nil? or known.value.strip.empty?
- # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
- # (this is a #ifdef: should be handled by the C parser)
- known = @constants_hash[constant.name]
+ constant.parent = self
+ known.is_alias_for ||= constant.is_alias_for
+ else
+ @constants_hash[constant.name] = constant
+ add_to @constants, constant
+ end
- if known
- known.comment = constant.comment if known.comment.empty?
+ constant
+ end
- known.value = constant.value if
- known.value.nil? or known.value.strip.empty?
+ ##
+ # Adds included module +include+ which should be an RDoc::Include
- constant.parent = self
- known.is_alias_for ||= constant.is_alias_for
- else
- @constants_hash[constant.name] = constant
- add_to @constants, constant
- end
+ def add_include(include)
+ add_to @includes, include
- constant
- end
+ include
+ end
- ##
- # Adds included module +include+ which should be an RDoc::Include
+ ##
+ # Adds extension module +ext+ which should be an RDoc::Extend
- def add_include(include)
- add_to @includes, include
+ def add_extend(ext)
+ add_to @extends, ext
- include
- end
+ ext
+ end
- ##
- # Adds extension module +ext+ which should be an RDoc::Extend
+ ##
+ # Adds +method+ if not already there. If it is (as method or attribute),
+ # updates the comment if it was empty.
- def add_extend(ext)
- add_to @extends, ext
+ def add_method(method)
+ return method unless @document_self
- ext
- end
+ # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
+ key = method.pretty_name
+ known = @methods_hash[key]
- ##
- # Adds +method+ if not already there. If it is (as method or attribute),
- # updates the comment if it was empty.
-
- def add_method(method)
- return method unless @document_self
-
- # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
- key = method.pretty_name
- known = @methods_hash[key]
-
- if known
- if @store # otherwise we are loading
- known.comment = method.comment if known.comment.empty?
- previously = ", previously in #{known.file}" unless
- method.file == known.file
- @store.options.warn \
- "Duplicate method #{known.full_name} in #{method.file}#{previously}"
+ if known
+ if @store # otherwise we are loading
+ known.comment = method.comment if known.comment.empty?
+ previously = ", previously in #{known.file}" unless
+ method.file == known.file
+ @store.options.warn \
+ "Duplicate method #{known.full_name} in #{method.file}#{previously}"
+ end
+ else
+ @methods_hash[key] = method
+ add_to @method_list, method
+ resolve_aliases method
end
- else
- @methods_hash[key] = method
- add_to @method_list, method
- resolve_aliases method
- end
- method
- end
+ method
+ end
- ##
- # Returns the owner context and local name for +constant_path+, creating
- # missing namespace modules. A leading +::+ resolves from the top-level.
- # This only resolves explicit context-tree paths; RDoc::Parser::Ruby has
- # parser-local lexical helpers for Ruby's nesting-dependent lookup.
+ ##
+ # Returns the owner context and local name for +constant_path+, creating
+ # missing namespace modules. A leading +::+ resolves from the top-level.
+ # This only resolves explicit context-tree paths; RDoc::Parser::Ruby has
+ # parser-local lexical helpers for Ruby's nesting-dependent lookup.
- def find_or_create_constant_owner_for_path(constant_path) # :nodoc:
- constant_path = constant_path.to_s
- owner = constant_path.start_with?('::') ? top_level : self
- constant_path = constant_path.delete_prefix('::')
+ def find_or_create_constant_owner_for_path(constant_path) # :nodoc:
+ constant_path = constant_path.to_s
+ owner = constant_path.start_with?('::') ? top_level : self
+ constant_path = constant_path.delete_prefix('::')
- owner_path, separator, name = constant_path.rpartition('::')
- owner = owner.find_or_create_namespace_path owner_path unless separator.empty?
+ owner_path, separator, name = constant_path.rpartition('::')
+ owner = owner.find_or_create_namespace_path owner_path unless separator.empty?
- [owner, name]
- end
+ [owner, name]
+ end
- ##
- # Finds or creates the module namespace path under this context.
+ ##
+ # Finds or creates the module namespace path under this context.
- def find_or_create_namespace_path(path) # :nodoc:
- path.to_s.split('::').inject(self) do |owner, name|
- owner.classes_hash[name] ||
- owner.modules_hash[name] ||
- owner.add_module(RDoc::NormalModule, name)
+ def find_or_create_namespace_path(path) # :nodoc:
+ path.to_s.split('::').inject(self) do |owner, name|
+ owner.classes_hash[name] ||
+ owner.modules_hash[name] ||
+ owner.add_module(NormalModule, name)
+ end
end
- end
- ##
- # Adds a module named +name+. If RDoc already knows +name+ is a class then
- # that class is returned instead. See also #add_class.
+ ##
+ # Adds a module named +name+. If RDoc already knows +name+ is a class then
+ # that class is returned instead. See also #add_class.
- def add_module(class_type, name)
- if name.to_s.include?('::')
- owner, name = find_or_create_constant_owner_for_path name
- return owner.add_module class_type, name unless owner == self
- end
+ def add_module(class_type, name)
+ if name.to_s.include?('::')
+ owner, name = find_or_create_constant_owner_for_path name
+ return owner.add_module class_type, name unless owner == self
+ end
- mod = @classes[name] || @modules[name]
- return mod if mod
+ mod = @classes[name] || @modules[name]
+ return mod if mod
- full_name = child_name name
- mod = @store.modules_hash[full_name] || class_type.new(name)
+ full_name = child_name name
+ mod = @store.modules_hash[full_name] || class_type.new(name)
- add_class_or_module mod, @modules, @store.modules_hash
- end
+ add_class_or_module mod, @modules, @store.modules_hash
+ end
- ##
- # Adds a module by +RDoc::NormalModule+ instance. See also #add_module.
+ ##
+ # Adds a module by +RDoc::NormalModule+ instance. See also #add_module.
- def add_module_by_normal_module(mod)
- add_class_or_module mod, @modules, @store.modules_hash
- end
+ def add_module_by_normal_module(mod)
+ add_class_or_module mod, @modules, @store.modules_hash
+ end
- ##
- # Adds an alias from +from+ (a class or module) to the constant +to+ which
- # was defined in +file+.
+ ##
+ # Adds an alias from +from+ (a class or module) to the constant +to+ which
+ # was defined in +file+.
+
+ def add_module_alias(from, to, file)
+ return from if @done_documenting
- def add_module_alias(from, to, file)
- return from if @done_documenting
+ to_full_name = child_name to.name
- to_full_name = child_name to.name
+ # if we already know this name, don't register an alias:
+ # see the metaprogramming in lib/active_support/basic_object.rb,
+ # where we already know BasicObject is a class when we find
+ # BasicObject = BlankSlate
+ return from if @store.find_class_or_module to_full_name
- # if we already know this name, don't register an alias:
- # see the metaprogramming in lib/active_support/basic_object.rb,
- # where we already know BasicObject is a class when we find
- # BasicObject = BlankSlate
- return from if @store.find_class_or_module to_full_name
+ new_to = from.dup
+ new_to.name = to.name
+ new_to.full_name = nil
+ new_to.is_alias_for = from
+
+ if new_to.module?
+ @store.modules_hash[to_full_name] = new_to
+ @modules[to.name] = new_to
+ else
+ @store.classes_hash[to_full_name] = new_to
+ @classes[to.name] = new_to
+ end
- new_to = from.dup
- new_to.name = to.name
- new_to.full_name = nil
- new_to.is_alias_for = from
+ # Registers a constant for this alias. The constant value and comment
+ # will be updated later, when the Ruby parser adds the constant
+ const = Constant.new to.name, nil, new_to.comment
+ const.record_location file
+ const.is_alias_for = from
+ add_constant const
- if new_to.module?
- @store.modules_hash[to_full_name] = new_to
- @modules[to.name] = new_to
- else
- @store.classes_hash[to_full_name] = new_to
- @classes[to.name] = new_to
+ new_to
end
- # Registers a constant for this alias. The constant value and comment
- # will be updated later, when the Ruby parser adds the constant
- const = RDoc::Constant.new to.name, nil, new_to.comment
- const.record_location file
- const.is_alias_for = from
- add_constant const
+ ##
+ # Adds +require+ to this context's top level
- new_to
- end
+ def add_require(require)
+ return require unless @document_self
- ##
- # Adds +require+ to this context's top level
+ if TopLevel === self
+ add_to @requires, require
+ else
+ parent.add_require require
+ end
+ end
- def add_require(require)
- return require unless @document_self
+ ##
+ # Returns a section with +title+, creating it if it doesn't already exist.
+ # +comment+ will be appended to the section's comment.
+ #
+ # A section with a +title+ of +nil+ will return the default section.
+ #
+ # See also RDoc::Context::Section
+
+ def add_section(title, comment = nil)
+ if section = @sections[title]
+ section.add_comment comment if comment
+ else
+ section = Section.new self, title, comment, @store
+ @sections[title] = section
+ end
- if RDoc::TopLevel === self
- add_to @requires, require
- else
- parent.add_require require
+ section
end
- end
- ##
- # Returns a section with +title+, creating it if it doesn't already exist.
- # +comment+ will be appended to the section's comment.
- #
- # A section with a +title+ of +nil+ will return the default section.
- #
- # See also RDoc::Context::Section
-
- def add_section(title, comment = nil)
- if section = @sections[title]
- section.add_comment comment if comment
- else
- section = Section.new self, title, comment, @store
- @sections[title] = section
- end
-
- section
- end
+ ##
+ # Adds +thing+ to the collection +array+
- ##
- # Adds +thing+ to the collection +array+
+ def add_to(array, thing)
+ array << thing if @document_self
- def add_to(array, thing)
- array << thing if @document_self
+ thing.parent = self
+ thing.store = @store if @store
+ thing.section = current_section
+ end
- thing.parent = self
- thing.store = @store if @store
- thing.section = current_section
- end
+ ##
+ # Is there any content?
+ #
+ # This means any of: comment, aliases, methods, attributes, external
+ # aliases, require, constant.
+ #
+ # Includes and extends are also checked unless includes == false.
+
+ def any_content(includes = true)
+ @any_content ||= !(
+ @comment.empty? &&
+ @method_list.empty? &&
+ @attributes.empty? &&
+ @aliases.empty? &&
+ @external_aliases.empty? &&
+ @requires.empty? &&
+ @constants.empty?
+ )
+ @any_content || (includes && !(@includes + @extends).empty? )
+ end
- ##
- # Is there any content?
- #
- # This means any of: comment, aliases, methods, attributes, external
- # aliases, require, constant.
- #
- # Includes and extends are also checked unless includes == false.
-
- def any_content(includes = true)
- @any_content ||= !(
- @comment.empty? &&
- @method_list.empty? &&
- @attributes.empty? &&
- @aliases.empty? &&
- @external_aliases.empty? &&
- @requires.empty? &&
- @constants.empty?
- )
- @any_content || (includes && !(@includes + @extends).empty? )
- end
+ ##
+ # Creates the full name for a child with +name+
- ##
- # Creates the full name for a child with +name+
+ def child_name(name)
+ if name =~ /^:+/
+ $' #'
+ elsif TopLevel === self
+ name
+ else
+ "#{self.full_name}::#{name}"
+ end
+ end
- def child_name(name)
- if name =~ /^:+/
- $' #'
- elsif RDoc::TopLevel === self
- name
- else
- "#{self.full_name}::#{name}"
+ ##
+ # Class methods
+
+ def class_method_list
+ method_list.select { |a| a.singleton }
end
- end
- ##
- # Class methods
+ ##
+ # Array of classes in this context
- def class_method_list
- method_list.select { |a| a.singleton }
- end
+ def classes
+ @classes.values
+ end
- ##
- # Array of classes in this context
+ ##
+ # All classes and modules in this namespace
- def classes
- @classes.values
- end
+ def classes_and_modules
+ classes + modules
+ end
- ##
- # All classes and modules in this namespace
+ ##
+ # Hash of classes keyed by class name
- def classes_and_modules
- classes + modules
- end
+ def classes_hash
+ @classes
+ end
- ##
- # Hash of classes keyed by class name
+ ##
+ # The current documentation section that new items will be added to. If
+ # temporary_section is available it will be used.
- def classes_hash
- @classes
- end
+ def current_section
+ if section = @temporary_section
+ @temporary_section = nil
+ else
+ section = @current_section
+ end
- ##
- # The current documentation section that new items will be added to. If
- # temporary_section is available it will be used.
+ section
+ end
- def current_section
- if section = @temporary_section
- @temporary_section = nil
- else
- section = @current_section
+ def display(method_attr) # :nodoc:
+ if method_attr.is_a? Attr
+ "#{method_attr.definition} #{method_attr.pretty_name}"
+ else
+ "method #{method_attr.pretty_name}"
+ end
end
- section
- end
+ ##
+ # Iterator for ancestors for duck-typing. Does nothing. See
+ # RDoc::ClassModule#each_ancestor.
+ #
+ # This method exists to make it easy to work with Context subclasses that
+ # aren't part of RDoc.
- def display(method_attr) # :nodoc:
- if method_attr.is_a? RDoc::Attr
- "#{method_attr.definition} #{method_attr.pretty_name}"
- else
- "method #{method_attr.pretty_name}"
+ def each_ancestor(&_) # :nodoc:
end
- end
- ##
- # Iterator for ancestors for duck-typing. Does nothing. See
- # RDoc::ClassModule#each_ancestor.
- #
- # This method exists to make it easy to work with Context subclasses that
- # aren't part of RDoc.
+ ##
+ # Iterator for classes and modules
- def each_ancestor(&_) # :nodoc:
- end
+ def each_classmodule(&block) # :yields: module
+ classes_and_modules.sort.each(&block)
+ end
- ##
- # Iterator for classes and modules
+ ##
+ # Iterator for methods
- def each_classmodule(&block) # :yields: module
- classes_and_modules.sort.each(&block)
- end
+ def each_method # :yields: method
+ return enum_for __method__ unless block_given?
- ##
- # Iterator for methods
+ @method_list.sort.each { |m| yield m }
+ end
- def each_method # :yields: method
- return enum_for __method__ unless block_given?
+ ##
+ # Iterator for each section's contents sorted by title. The +section+, the
+ # section's +constants+ and the sections +attributes+ are yielded. The
+ # +constants+ and +attributes+ collections are sorted.
+ #
+ # To retrieve methods in a section use #methods_by_type with the optional
+ # +section+ parameter.
+ #
+ # NOTE: Do not edit collections yielded by this method
- @method_list.sort.each { |m| yield m }
- end
+ def each_section # :yields: section, constants, attributes
+ return enum_for __method__ unless block_given?
- ##
- # Iterator for each section's contents sorted by title. The +section+, the
- # section's +constants+ and the sections +attributes+ are yielded. The
- # +constants+ and +attributes+ collections are sorted.
- #
- # To retrieve methods in a section use #methods_by_type with the optional
- # +section+ parameter.
- #
- # NOTE: Do not edit collections yielded by this method
+ constants = @constants.group_by do |constant| constant.section end
+ attributes = @attributes.group_by do |attribute| attribute.section end
- def each_section # :yields: section, constants, attributes
- return enum_for __method__ unless block_given?
+ constants.default = []
+ attributes.default = []
- constants = @constants.group_by do |constant| constant.section end
- attributes = @attributes.group_by do |attribute| attribute.section end
+ sort_sections.each do |section|
+ yield section, constants[section].select(&:display?).sort, attributes[section].select(&:display?).sort
+ end
+ end
- constants.default = []
- attributes.default = []
+ ##
+ # Finds an attribute +name+ with singleton value +singleton+.
- sort_sections.each do |section|
- yield section, constants[section].select(&:display?).sort, attributes[section].select(&:display?).sort
+ def find_attribute(name, singleton)
+ name = $1 if name =~ /^(.*)=$/
+ @attributes.find { |a| a.name == name && a.singleton == singleton }
end
- end
- ##
- # Finds an attribute +name+ with singleton value +singleton+.
+ ##
+ # Finds an attribute with +name+ in this context
- def find_attribute(name, singleton)
- name = $1 if name =~ /^(.*)=$/
- @attributes.find { |a| a.name == name && a.singleton == singleton }
- end
+ def find_attribute_named(name)
+ case name
+ when /\A#/
+ find_attribute name[1..-1], false
+ when /\A::/
+ find_attribute name[2..-1], true
+ else
+ @attributes.find { |a| a.name == name }
+ end
+ end
- ##
- # Finds an attribute with +name+ in this context
+ ##
+ # Finds a class method with +name+ in this context
- def find_attribute_named(name)
- case name
- when /\A#/
- find_attribute name[1..-1], false
- when /\A::/
- find_attribute name[2..-1], true
- else
- @attributes.find { |a| a.name == name }
+ def find_class_method_named(name)
+ @method_list.find { |meth| meth.singleton && meth.name == name }
end
- end
- ##
- # Finds a class method with +name+ in this context
+ ##
+ # Finds a constant with +name+ in this context
- def find_class_method_named(name)
- @method_list.find { |meth| meth.singleton && meth.name == name }
- end
+ def find_constant_named(name)
+ @constants.find do |m|
+ m.name == name || m.full_name == name
+ end
+ end
- ##
- # Finds a constant with +name+ in this context
+ ##
+ # Tries to find a module at a higher scope.
+ # But parent is not always a higher module nesting scope, so the result is not correct.
+ # Parent chain can only represent last-opened nesting, and may be broken in some cases.
+ # The Ruby parser does not represent module nesting with the parent chain.
- def find_constant_named(name)
- @constants.find do |m|
- m.name == name || m.full_name == name
+ def find_enclosing_module_named(name)
+ parent && parent.find_module_named(name)
end
- end
- ##
- # Tries to find a module at a higher scope.
- # But parent is not always a higher module nesting scope, so the result is not correct.
- # Parent chain can only represent last-opened nesting, and may be broken in some cases.
- # The Ruby parser does not represent module nesting with the parent chain.
+ ##
+ # Finds an external alias +name+ with singleton value +singleton+.
- def find_enclosing_module_named(name)
- parent && parent.find_module_named(name)
- end
+ def find_external_alias(name, singleton)
+ @external_aliases.find { |m| m.name == name && m.singleton == singleton }
+ end
- ##
- # Finds an external alias +name+ with singleton value +singleton+.
+ ##
+ # Finds an external alias with +name+ in this context
- def find_external_alias(name, singleton)
- @external_aliases.find { |m| m.name == name && m.singleton == singleton }
- end
+ def find_external_alias_named(name)
+ case name
+ when /\A#/
+ find_external_alias name[1..-1], false
+ when /\A::/
+ find_external_alias name[2..-1], true
+ else
+ @external_aliases.find { |a| a.name == name }
+ end
+ end
- ##
- # Finds an external alias with +name+ in this context
+ ##
+ # Finds an instance method with +name+ in this context
- def find_external_alias_named(name)
- case name
- when /\A#/
- find_external_alias name[1..-1], false
- when /\A::/
- find_external_alias name[2..-1], true
- else
- @external_aliases.find { |a| a.name == name }
+ def find_instance_method_named(name)
+ @method_list.find { |meth| !meth.singleton && meth.name == name }
end
- end
- ##
- # Finds an instance method with +name+ in this context
+ ##
+ # Finds a method, constant, attribute, external alias, module or file
+ # named +symbol+ in this context.
+
+ def find_local_symbol(symbol)
+ find_method_named(symbol) or
+ find_constant_named(symbol) or
+ find_attribute_named(symbol) or
+ find_external_alias_named(symbol) or
+ find_module_named(symbol) or
+ @store.find_file_named(symbol)
+ end
- def find_instance_method_named(name)
- @method_list.find { |meth| !meth.singleton && meth.name == name }
- end
+ ##
+ # Finds a method named +name+ with singleton value +singleton+.
- ##
- # Finds a method, constant, attribute, external alias, module or file
- # named +symbol+ in this context.
-
- def find_local_symbol(symbol)
- find_method_named(symbol) or
- find_constant_named(symbol) or
- find_attribute_named(symbol) or
- find_external_alias_named(symbol) or
- find_module_named(symbol) or
- @store.find_file_named(symbol)
- end
+ def find_method(name, singleton)
+ @method_list.find { |m|
+ if m.singleton
+ m.name == name && m.singleton == singleton
+ else
+ m.name == name && !m.singleton && !singleton
+ end
+ }
+ end
- ##
- # Finds a method named +name+ with singleton value +singleton+.
+ ##
+ # Finds a instance or module method with +name+ in this context
- def find_method(name, singleton)
- @method_list.find { |m|
- if m.singleton
- m.name == name && m.singleton == singleton
+ def find_method_named(name)
+ case name
+ when /\A#/
+ find_method name[1..-1], false
+ when /\A::/
+ find_method name[2..-1], true
else
- m.name == name && !m.singleton && !singleton
+ @method_list.find { |meth| meth.name == name }
end
- }
- end
+ end
- ##
- # Finds a instance or module method with +name+ in this context
+ ##
+ # Find a module with +name+ trying to using ruby's scoping rules.
+ # find_enclosing_module_named cannot use ruby's scoping so the result is not correct.
- def find_method_named(name)
- case name
- when /\A#/
- find_method name[1..-1], false
- when /\A::/
- find_method name[2..-1], true
- else
- @method_list.find { |meth| meth.name == name }
+ def find_module_named(name)
+ res = get_module_named(name)
+ return res if res
+ return self if self.name == name
+ find_enclosing_module_named name
end
- end
- ##
- # Find a module with +name+ trying to using ruby's scoping rules.
- # find_enclosing_module_named cannot use ruby's scoping so the result is not correct.
-
- def find_module_named(name)
- res = get_module_named(name)
- return res if res
- return self if self.name == name
- find_enclosing_module_named name
- end
+ # Get a module named +name+ in this context
+ # Don't look up for higher module nesting scopes. RDoc::Context doesn't have that information.
- # Get a module named +name+ in this context
- # Don't look up for higher module nesting scopes. RDoc::Context doesn't have that information.
-
- def get_module_named(name)
- @modules[name] || @classes[name]
- end
+ def get_module_named(name)
+ @modules[name] || @classes[name]
+ end
- ##
- # Look up +symbol+, first as a module, then as a local symbol.
+ ##
+ # Look up +symbol+, first as a module, then as a local symbol.
- def find_symbol(symbol)
- find_symbol_module(symbol) || find_local_symbol(symbol)
- end
+ def find_symbol(symbol)
+ find_symbol_module(symbol) || find_local_symbol(symbol)
+ end
- ##
- # Look up a module named +symbol+.
-
- def find_symbol_module(symbol)
- result = nil
-
- # look for a class or module 'symbol'
- case symbol
- when /^::/
- result = @store.find_class_or_module symbol
- when /^(\w+):+(.+)$/
- suffix = $2
- top = $1
- searched = self
- while searched do
- mod = searched.find_module_named(top)
- break unless mod
- result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
- break if result || searched.is_a?(RDoc::TopLevel)
- searched = searched.parent
- end
- else
- searched = self
- while searched do
- result = searched.find_module_named(symbol)
- break if result || searched.is_a?(RDoc::TopLevel)
- searched = searched.parent
+ ##
+ # Look up a module named +symbol+.
+
+ def find_symbol_module(symbol)
+ result = nil
+
+ # look for a class or module 'symbol'
+ case symbol
+ when /^::/
+ result = @store.find_class_or_module symbol
+ when /^(\w+):+(.+)$/
+ suffix = $2
+ top = $1
+ searched = self
+ while searched do
+ mod = searched.find_module_named(top)
+ break unless mod
+ result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
+ break if result || searched.is_a?(TopLevel)
+ searched = searched.parent
+ end
+ else
+ searched = self
+ while searched do
+ result = searched.find_module_named(symbol)
+ break if result || searched.is_a?(TopLevel)
+ searched = searched.parent
+ end
end
+
+ result
end
- result
- end
+ ##
+ # The full name for this context. This method is overridden by subclasses.
- ##
- # The full name for this context. This method is overridden by subclasses.
+ def full_name
+ '(unknown)'
+ end
- def full_name
- '(unknown)'
- end
+ ##
+ # Does this context and its methods and constants all have documentation?
+ #
+ # (Yes, fully documented doesn't mean everything.)
- ##
- # Does this context and its methods and constants all have documentation?
- #
- # (Yes, fully documented doesn't mean everything.)
-
- def fully_documented?
- documented? and
- attributes.all? { |a| a.documented? } and
- method_list.all? { |m| m.documented? } and
- constants.all? { |c| c.documented? }
- end
+ def fully_documented?
+ documented? and
+ attributes.all? { |a| a.documented? } and
+ method_list.all? { |m| m.documented? } and
+ constants.all? { |c| c.documented? }
+ end
- ##
- # URL for this with a +prefix+
+ ##
+ # URL for this with a +prefix+
- def http_url
- path = name_for_path
- path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<
- path = path.split('::')
+ def http_url
+ path = name_for_path
+ path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<
+ path = path.split('::')
- File.join(*path.compact) + '.html'
- end
+ File.join(*path.compact) + '.html'
+ end
- ##
- # Instance methods
+ ##
+ # Instance methods
- def instance_methods
- method_list.reject { |a| a.singleton }
- end
+ def instance_methods
+ method_list.reject { |a| a.singleton }
+ end
- ##
- # Breaks method_list into a nested hash by type ('class' or
- # 'instance') and visibility (+:public+, +:protected+, +:private+).
- #
- # If +section+ is provided only methods in that RDoc::Context::Section will
- # be returned.
-
- def methods_by_type(section = nil)
- methods = {}
-
- TYPES.each do |type|
- visibilities = {}
- RDoc::VISIBILITIES.each do |vis|
- visibilities[vis] = []
+ ##
+ # Breaks method_list into a nested hash by type ('class' or
+ # 'instance') and visibility (+:public+, +:protected+, +:private+).
+ #
+ # If +section+ is provided only methods in that RDoc::Context::Section will
+ # be returned.
+
+ def methods_by_type(section = nil)
+ methods = {}
+
+ TYPES.each do |type|
+ visibilities = {}
+ VISIBILITIES.each do |vis|
+ visibilities[vis] = []
+ end
+
+ methods[type] = visibilities
end
- methods[type] = visibilities
- end
+ each_method do |method|
+ next if section and not method.section == section
+ methods[method.type][method.visibility] << method
+ end
- each_method do |method|
- next if section and not method.section == section
- methods[method.type][method.visibility] << method
+ methods
end
- methods
- end
+ ##
+ # Yields AnyMethod and Attr entries matching the list of names in +methods+.
- ##
- # Yields AnyMethod and Attr entries matching the list of names in +methods+.
+ def methods_matching(methods, singleton = false, &block)
+ (@method_list + @attributes).each do |m|
+ yield m if methods.include?(m.name) and m.singleton == singleton
+ end
- def methods_matching(methods, singleton = false, &block)
- (@method_list + @attributes).each do |m|
- yield m if methods.include?(m.name) and m.singleton == singleton
+ each_ancestor do |parent|
+ parent.methods_matching(methods, singleton, &block)
+ end
end
- each_ancestor do |parent|
- parent.methods_matching(methods, singleton, &block)
+ ##
+ # Array of modules in this context
+
+ def modules
+ @modules.values
end
- end
- ##
- # Array of modules in this context
+ ##
+ # Hash of modules keyed by module name
- def modules
- @modules.values
- end
+ def modules_hash
+ @modules
+ end
- ##
- # Hash of modules keyed by module name
+ ##
+ # Name to use to generate the url.
+ # #full_name by default.
- def modules_hash
- @modules
- end
+ def name_for_path
+ full_name
+ end
- ##
- # Name to use to generate the url.
- # #full_name by default.
+ ##
+ # Record +top_level+ as a file +self+ is in.
- def name_for_path
- full_name
- end
+ def record_location(top_level)
+ @in_files << top_level unless @in_files.include?(top_level)
+ end
- ##
- # Record +top_level+ as a file +self+ is in.
+ ##
+ # Should we remove this context from the documentation?
+ #
+ # The answer is yes if:
+ # * #received_nodoc is +true+
+ # * #any_content is +false+ (not counting includes)
+ # * All #includes are modules (not a string), and their module has
+ # #remove_from_documentation? == true
+ # * All classes and modules have #remove_from_documentation? == true
+
+ def remove_from_documentation?
+ # Contexts that are still ignored here were created inside a :stopdoc:
+ # region and never received documentable contents afterwards
+ @remove_from_documentation ||=
+ (@received_nodoc || @ignored) &&
+ !any_content(false) &&
+ @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
+ classes_and_modules.all? { |cm| cm.remove_from_documentation? }
+ end
- def record_location(top_level)
- @in_files << top_level unless @in_files.include?(top_level)
- end
+ ##
+ # Removes methods and attributes with a visibility less than +min_visibility+.
+ #--
+ # TODO mark the visibility of attributes in the template (if not public?)
- ##
- # Should we remove this context from the documentation?
- #
- # The answer is yes if:
- # * #received_nodoc is +true+
- # * #any_content is +false+ (not counting includes)
- # * All #includes are modules (not a string), and their module has
- # #remove_from_documentation? == true
- # * All classes and modules have #remove_from_documentation? == true
-
- def remove_from_documentation?
- # Contexts that are still ignored here were created inside a :stopdoc:
- # region and never received documentable contents afterwards
- @remove_from_documentation ||=
- (@received_nodoc || @ignored) &&
- !any_content(false) &&
- @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
- classes_and_modules.all? { |cm| cm.remove_from_documentation? }
- end
+ def remove_invisible(min_visibility)
+ return if [:private, :nodoc].include? min_visibility
+ remove_invisible_in @method_list, min_visibility
+ remove_invisible_in @attributes, min_visibility
+ remove_invisible_in @constants, min_visibility
+ end
- ##
- # Removes methods and attributes with a visibility less than +min_visibility+.
- #--
- # TODO mark the visibility of attributes in the template (if not public?)
-
- def remove_invisible(min_visibility)
- return if [:private, :nodoc].include? min_visibility
- remove_invisible_in @method_list, min_visibility
- remove_invisible_in @attributes, min_visibility
- remove_invisible_in @constants, min_visibility
- end
+ ##
+ # Only called when min_visibility == :public or :private
- ##
- # Only called when min_visibility == :public or :private
+ def remove_invisible_in(array, min_visibility) # :nodoc:
+ if min_visibility == :public
+ array.reject! { |e|
+ e.visibility != :public and not e.force_documentation
+ }
+ else
+ array.reject! { |e|
+ e.visibility == :private and not e.force_documentation
+ }
+ end
+ end
- def remove_invisible_in(array, min_visibility) # :nodoc:
- if min_visibility == :public
- array.reject! { |e|
- e.visibility != :public and not e.force_documentation
- }
- else
- array.reject! { |e|
- e.visibility == :private and not e.force_documentation
- }
+ ##
+ # Tries to resolve unmatched aliases when a method or attribute has just
+ # been added.
+
+ def resolve_aliases(added)
+ # resolve any pending unmatched aliases
+ key = added.pretty_name
+ unmatched_alias_list = @unmatched_alias_lists[key]
+ return unless unmatched_alias_list
+ unmatched_alias_list.each do |unmatched_alias|
+ added.add_alias unmatched_alias, self
+ @external_aliases.delete unmatched_alias
+ end
+ @unmatched_alias_lists.delete key
end
- end
- ##
- # Tries to resolve unmatched aliases when a method or attribute has just
- # been added.
-
- def resolve_aliases(added)
- # resolve any pending unmatched aliases
- key = added.pretty_name
- unmatched_alias_list = @unmatched_alias_lists[key]
- return unless unmatched_alias_list
- unmatched_alias_list.each do |unmatched_alias|
- added.add_alias unmatched_alias, self
- @external_aliases.delete unmatched_alias
- end
- @unmatched_alias_lists.delete key
- end
+ ##
+ # Returns RDoc::Context::Section objects referenced in this context for use
+ # in a table of contents.
- ##
- # Returns RDoc::Context::Section objects referenced in this context for use
- # in a table of contents.
+ def section_contents
+ used_sections = {}
- def section_contents
- used_sections = {}
+ each_method do |method|
+ next unless method.display?
- each_method do |method|
- next unless method.display?
+ used_sections[method.section] = true
+ end
- used_sections[method.section] = true
- end
+ # order found sections
+ sections = sort_sections.select do |section|
+ used_sections[section]
+ end
- # order found sections
- sections = sort_sections.select do |section|
- used_sections[section]
+ # only the default section is used
+ return [] if
+ sections.length == 1 and not sections.first.title
+
+ sections
end
- # only the default section is used
- return [] if
- sections.length == 1 and not sections.first.title
+ ##
+ # Sections in this context
- sections
- end
+ def sections
+ @sections.values
+ end
- ##
- # Sections in this context
+ def sections_hash # :nodoc:
+ @sections
+ end
- def sections
- @sections.values
- end
+ ##
+ # Sets the current section to a section with +title+. See also #add_section
- def sections_hash # :nodoc:
- @sections
- end
+ def set_current_section(title, comment)
+ @current_section = add_section title, comment
+ end
- ##
- # Sets the current section to a section with +title+. See also #add_section
+ ##
+ # Given an array +methods+ of method names, set the visibility of each to
+ # +visibility+
- def set_current_section(title, comment)
- @current_section = add_section title, comment
- end
+ def set_visibility_for(methods, visibility, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = visibility
+ end
+ end
- ##
- # Given an array +methods+ of method names, set the visibility of each to
- # +visibility+
+ ##
+ # Given an array +names+ of constants, set the visibility of each constant to
+ # +visibility+
- def set_visibility_for(methods, visibility, singleton = false)
- methods_matching methods, singleton do |m|
- m.visibility = visibility
+ def set_constant_visibility_for(names, visibility)
+ names.each do |name|
+ constant = @constants_hash[name] or next
+ constant.visibility = visibility
+ end
end
- end
- ##
- # Given an array +names+ of constants, set the visibility of each constant to
- # +visibility+
+ ##
+ # Sorts sections alphabetically (default) or in TomDoc fashion (none,
+ # Public, Internal, Deprecated)
- def set_constant_visibility_for(names, visibility)
- names.each do |name|
- constant = @constants_hash[name] or next
- constant.visibility = visibility
- end
- end
+ def sort_sections
+ titles = @sections.map { |title, _| title }
- ##
- # Sorts sections alphabetically (default) or in TomDoc fashion (none,
- # Public, Internal, Deprecated)
-
- def sort_sections
- titles = @sections.map { |title, _| title }
-
- if titles.length > 1 and
- TOMDOC_TITLES_SORT ==
- (titles | TOMDOC_TITLES).sort_by { |title| title.to_s }
- @sections.values_at(*TOMDOC_TITLES).compact
- else
- @sections.sort_by { |title, _|
- title.to_s
- }.map { |_, section|
- section
- }
+ if titles.length > 1 and
+ TOMDOC_TITLES_SORT ==
+ (titles | TOMDOC_TITLES).sort_by { |title| title.to_s }
+ @sections.values_at(*TOMDOC_TITLES).compact
+ else
+ @sections.sort_by { |title, _|
+ title.to_s
+ }.map { |_, section|
+ section
+ }
+ end
end
- end
- def to_s # :nodoc:
- "#{self.class.name} #{self.full_name}"
- end
+ def to_s # :nodoc:
+ "#{self.class.name} #{self.full_name}"
+ end
- ##
- # Return the TopLevel that owns us
- #--
- # FIXME we can be 'owned' by several TopLevel (see #record_location &
- # #in_files)
-
- def top_level
- return @top_level if defined? @top_level
- @top_level = self
- @top_level = @top_level.parent until RDoc::TopLevel === @top_level
- @top_level
- end
+ ##
+ # Return the TopLevel that owns us
+ #--
+ # FIXME we can be 'owned' by several TopLevel (see #record_location &
+ # #in_files)
+
+ def top_level
+ return @top_level if defined? @top_level
+ @top_level = self
+ @top_level = @top_level.parent until TopLevel === @top_level
+ @top_level
+ end
- ##
- # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
+ ##
+ # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
- def upgrade_to_class(mod, class_type, enclosing)
- enclosing.modules_hash.delete mod.name
+ def upgrade_to_class(mod, class_type, enclosing)
+ enclosing.modules_hash.delete mod.name
- klass = RDoc::ClassModule.from_module class_type, mod
- klass.store = @store
+ klass = ClassModule.from_module class_type, mod
+ klass.store = @store
- # if it was there, then we keep it even if done_documenting
- @store.classes_hash[mod.full_name] = klass
- enclosing.classes_hash[mod.name] = klass
+ # if it was there, then we keep it even if done_documenting
+ @store.classes_hash[mod.full_name] = klass
+ enclosing.classes_hash[mod.name] = klass
- klass
- end
+ klass
+ end
- autoload :Section, "#{__dir__}/context/section"
+ autoload :Section, "#{__dir__}/context/section"
+ end
end
diff --git a/lib/rdoc/code_object/context/section.rb b/lib/rdoc/code_object/context/section.rb
index 2d2b78e465..2b0f4f6f7a 100644
--- a/lib/rdoc/code_object/context/section.rb
+++ b/lib/rdoc/code_object/context/section.rb
@@ -2,181 +2,185 @@
require 'cgi/escape'
require 'cgi/util' unless defined?(CGI::EscapeExt)
-##
-# A section of documentation like:
-#
-# # :section: The title
-# # The body
-#
-# Sections can be referenced multiple times and will be collapsed into a
-# single section.
+module RDoc
+ class Context
+ ##
+ # A section of documentation like:
+ #
+ # # :section: The title
+ # # The body
+ #
+ # Sections can be referenced multiple times and will be collapsed into a
+ # single section.
-class RDoc::Context::Section
+ class Section
- include RDoc::Text
+ include Text
- MARSHAL_VERSION = 0 # :nodoc:
+ MARSHAL_VERSION = 0 # :nodoc:
- ##
- # Section comments
+ ##
+ # Section comments
- attr_reader :comments
+ attr_reader :comments
- ##
- # Context this Section lives in
+ ##
+ # Context this Section lives in
- attr_reader :parent
+ attr_reader :parent
- ##
- # Section title
+ ##
+ # Section title
- attr_reader :title
+ attr_reader :title
- ##
- # The RDoc::Store for this object.
+ ##
+ # The RDoc::Store for this object.
- attr_reader :store
+ attr_reader :store
- ##
- # Creates a new section with +title+ and +comment+
+ ##
+ # Creates a new section with +title+ and +comment+
- def initialize(parent, title, comment, store = nil)
- @parent = parent
- @title = title ? title.strip : title
- @store = store
+ def initialize(parent, title, comment, store = nil)
+ @parent = parent
+ @title = title ? title.strip : title
+ @store = store
- @comments = []
+ @comments = []
- add_comment comment
- end
+ add_comment comment
+ end
- ##
- # Sections are equal when they have the same #title
+ ##
+ # Sections are equal when they have the same #title
- def ==(other)
- self.class === other and @title == other.title
- end
+ def ==(other)
+ self.class === other and @title == other.title
+ end
- alias eql? ==
+ alias eql? ==
- ##
- # Adds +comment+ to this section
+ ##
+ # Adds +comment+ to this section
- def add_comment(comment)
- Array(comment).each do |c|
- next if c.nil?
- raise TypeError, "unknown comment #{c.inspect}" unless RDoc::Comment === c
- @comments << c unless c.empty?
- end
- end
+ def add_comment(comment)
+ Array(comment).each do |c|
+ next if c.nil?
+ raise TypeError, "unknown comment #{c.inspect}" unless Comment === c
+ @comments << c unless c.empty?
+ end
+ end
- ##
- # Anchor reference for linking to this section using GitHub-style format.
- #
- # Examples:
- # "Section" -> "section"
- # "One Two" -> "one-two"
- # "[untitled]" -> "untitled"
+ ##
+ # Anchor reference for linking to this section using GitHub-style format.
+ #
+ # Examples:
+ # "Section" -> "section"
+ # "One Two" -> "one-two"
+ # "[untitled]" -> "untitled"
- def aref
- title = @title || '[untitled]'
+ def aref
+ title = @title || '[untitled]'
- RDoc::Text.to_anchor(title)
- end
+ Text.to_anchor(title)
+ end
- ##
- # Legacy anchor reference for backward compatibility.
- #
- # Examples:
- # "Section" -> "section"
- # "One Two" -> "one+two"
- # "[untitled]" -> "5Buntitled-5D"
+ ##
+ # Legacy anchor reference for backward compatibility.
+ #
+ # Examples:
+ # "Section" -> "section"
+ # "One Two" -> "one+two"
+ # "[untitled]" -> "5Buntitled-5D"
- def legacy_aref
- title = @title || '[untitled]'
+ def legacy_aref
+ title = @title || '[untitled]'
- CGI.escape(title).gsub('%', '-').sub(/^-/, '')
- end
+ CGI.escape(title).gsub('%', '-').sub(/^-/, '')
+ end
- def inspect # :nodoc:
- "#<%s:0x%x %p>" % [self.class, object_id, title]
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x %p>" % [self.class, object_id, title]
+ end
- def hash # :nodoc:
- @title.hash
- end
-
- ##
- # The files comments in this section come from
+ def hash # :nodoc:
+ @title.hash
+ end
- def in_files
- @comments.map(&:file)
- end
+ ##
+ # The files comments in this section come from
- ##
- # Serializes this Section. The title and parsed comment are saved, but not
- # the section parent which must be restored manually.
+ def in_files
+ @comments.map(&:file)
+ end
- def marshal_dump
- [
- MARSHAL_VERSION,
- @title,
- to_document,
- ]
- end
+ ##
+ # Serializes this Section. The title and parsed comment are saved, but not
+ # the section parent which must be restored manually.
- ##
- # De-serializes this Section. The section parent must be restored manually.
+ def marshal_dump
+ [
+ MARSHAL_VERSION,
+ @title,
+ to_document,
+ ]
+ end
- def marshal_load(array)
- @parent = nil
+ ##
+ # De-serializes this Section. The section parent must be restored manually.
- @title = array[1]
- @comments = array[2].parts.map { |doc| RDoc::Comment.from_document(doc) }
- end
+ def marshal_load(array)
+ @parent = nil
- ##
- # Parses +comment_location+ into an RDoc::Markup::Document composed of
- # multiple RDoc::Markup::Documents with their file set.
+ @title = array[1]
+ @comments = array[2].parts.map { |doc| Comment.from_document(doc) }
+ end
- def to_document
- RDoc::Markup::Document.new(*@comments.map(&:parse))
- end
+ ##
+ # Parses +comment_location+ into an RDoc::Markup::Document composed of
+ # multiple RDoc::Markup::Documents with their file set.
+
+ def to_document
+ Markup::Document.new(*@comments.map(&:parse))
+ end
+
+ ##
+ # The section's title, or 'Top Section' if the title is nil.
+ #
+ # This is used by the table of contents template so the name is silly.
+
+ def plain_html
+ @title || 'Top Section'
+ end
+
+ ##
+ # Section comment
+
+ def comment
+ return nil if @comments.empty?
+ Comment.from_document(to_document)
+ end
+
+ def description
+ return '' if @comments.empty?
+ markup comment
+ end
+
+ def language
+ @comments.first&.language
+ end
+
+ ##
+ # Removes a comment from this section if it is from the same file as
+ # +comment+
+
+ def remove_comment(target_comment)
+ @comments.delete_if do |stored_comment|
+ stored_comment.file == target_comment.file
+ end
+ end
- ##
- # The section's title, or 'Top Section' if the title is nil.
- #
- # This is used by the table of contents template so the name is silly.
-
- def plain_html
- @title || 'Top Section'
- end
-
- ##
- # Section comment
-
- def comment
- return nil if @comments.empty?
- RDoc::Comment.from_document(to_document)
- end
-
- def description
- return '' if @comments.empty?
- markup comment
- end
-
- def language
- @comments.first&.language
- end
-
- ##
- # Removes a comment from this section if it is from the same file as
- # +comment+
-
- def remove_comment(target_comment)
- @comments.delete_if do |stored_comment|
- stored_comment.file == target_comment.file
end
end
-
end
diff --git a/lib/rdoc/code_object/extend.rb b/lib/rdoc/code_object/extend.rb
index 7d57433de6..41ae8bf8b2 100644
--- a/lib/rdoc/code_object/extend.rb
+++ b/lib/rdoc/code_object/extend.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
-##
-# A Module extension to a class with \#extend
-#
-# RDoc::Extend.new 'Enumerable', 'comment ...'
+module RDoc
+ ##
+ # A Module extension to a class with \#extend
+ #
+ # RDoc::Extend.new 'Enumerable', 'comment ...'
-class RDoc::Extend < RDoc::Mixin
+ class Extend < Mixin
+ end
end
diff --git a/lib/rdoc/code_object/include.rb b/lib/rdoc/code_object/include.rb
index c3e0d45e47..d3d9c96aac 100644
--- a/lib/rdoc/code_object/include.rb
+++ b/lib/rdoc/code_object/include.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
-##
-# A Module included in a class with \#include
-#
-# RDoc::Include.new 'Enumerable', 'comment ...'
+module RDoc
+ ##
+ # A Module included in a class with \#include
+ #
+ # RDoc::Include.new 'Enumerable', 'comment ...'
-class RDoc::Include < RDoc::Mixin
+ class Include < Mixin
+ end
end
diff --git a/lib/rdoc/code_object/method_attr.rb b/lib/rdoc/code_object/method_attr.rb
index b7b53a0bac..a760a89aae 100644
--- a/lib/rdoc/code_object/method_attr.rb
+++ b/lib/rdoc/code_object/method_attr.rb
@@ -1,422 +1,424 @@
# frozen_string_literal: true
-##
-# Abstract class representing either a method or an attribute.
-
-class RDoc::MethodAttr < RDoc::CodeObject
-
- include Comparable
-
+module RDoc
##
- # Name of this method/attribute.
+ # Abstract class representing either a method or an attribute.
- attr_accessor :name
+ class MethodAttr < CodeObject
- ##
- # public, protected, private
+ include Comparable
- attr_accessor :visibility
+ ##
+ # Name of this method/attribute.
- ##
- # Is this a singleton method/attribute?
+ attr_accessor :name
- attr_accessor :singleton
+ ##
+ # public, protected, private
- ##
- # Array of other names for this method/attribute
+ attr_accessor :visibility
- attr_reader :aliases
+ ##
+ # Is this a singleton method/attribute?
- ##
- # The method/attribute we're aliasing
+ attr_accessor :singleton
- attr_accessor :is_alias_for
+ ##
+ # Array of other names for this method/attribute
- #--
- # The attributes below are for AnyMethod only.
- # They are left here for the time being to
- # allow ri to operate.
- # TODO modify ri to avoid calling these on attributes.
- #++
+ attr_reader :aliases
- ##
- # Parameters yielded by the called block
+ ##
+ # The method/attribute we're aliasing
- attr_reader :block_params
+ attr_accessor :is_alias_for
- ##
- # Parameters for this method
+ #--
+ # The attributes below are for AnyMethod only.
+ # They are left here for the time being to
+ # allow ri to operate.
+ # TODO modify ri to avoid calling these on attributes.
+ #++
- attr_accessor :params
+ ##
+ # Parameters yielded by the called block
- ##
- # Different ways to call this method
+ attr_reader :block_params
- attr_accessor :call_seq
+ ##
+ # Parameters for this method
- ##
- # RBS type signature lines from inline annotations or loaded .rbs files.
- # Each entry is one overload or type expression.
+ attr_accessor :params
- attr_accessor :type_signature_lines
+ ##
+ # Different ways to call this method
- ##
- # The call_seq or the param_seq with method name, if there is no call_seq.
+ attr_accessor :call_seq
- attr_reader :arglists
+ ##
+ # RBS type signature lines from inline annotations or loaded .rbs files.
+ # Each entry is one overload or type expression.
- ##
- # Creates a new MethodAttr with method or attribute
- # name +name+.
- #
- # Usually this is called by super from a subclass.
-
- def initialize(name, singleton: false)
- super()
-
- @name = name
-
- @aliases = []
- @is_alias_for = nil
- @parent_name = nil
- @singleton = singleton
- @visibility = :public
- @see = false
-
- @arglists = nil
- @block_params = nil
- @call_seq = nil
- @params = nil
- @type_signature_lines = nil
- end
+ attr_accessor :type_signature_lines
- ##
- # Resets cached data for the object so it can be rebuilt by accessor methods
+ ##
+ # The call_seq or the param_seq with method name, if there is no call_seq.
- def initialize_copy(other) # :nodoc:
- @full_name = nil
- end
+ attr_reader :arglists
- def initialize_visibility # :nodoc:
- super
- @see = nil
- end
+ ##
+ # Creates a new MethodAttr with method or attribute
+ # name +name+.
+ #
+ # Usually this is called by super from a subclass.
- ##
- # Order by #singleton then #name
+ def initialize(name, singleton: false)
+ super()
- def <=>(other)
- return unless other.respond_to?(:singleton) &&
- other.respond_to?(:name)
+ @name = name
- [@singleton ? 0 : 1, name_ord_range, name] <=>
- [other.singleton ? 0 : 1, other.name_ord_range, other.name]
- end
+ @aliases = []
+ @is_alias_for = nil
+ @parent_name = nil
+ @singleton = singleton
+ @visibility = :public
+ @see = false
- def ==(other) # :nodoc:
- equal?(other) or self.class == other.class and full_name == other.full_name
- end
+ @arglists = nil
+ @block_params = nil
+ @call_seq = nil
+ @params = nil
+ @type_signature_lines = nil
+ end
- ##
- # A method/attribute is documented if any of the following is true:
- # - it was marked with :nodoc:;
- # - it has a comment;
- # - it is an alias for a documented method;
- # - it has a +#see+ method that is documented.
-
- def documented?
- super or
- (is_alias_for and is_alias_for.documented?) or
- (see and see.documented?)
- end
+ ##
+ # Resets cached data for the object so it can be rebuilt by accessor methods
- ##
- # A method/attribute to look at,
- # in particular if this method/attribute has no documentation.
- #
- # It can be a method/attribute of the superclass or of an included module,
- # including the Kernel module, which is always appended to the included
- # modules.
- #
- # Returns +nil+ if there is no such method/attribute.
- # The +#is_alias_for+ method/attribute, if any, is not included.
- #
- # Templates may generate a "see also ..." if this method/attribute
- # has documentation, and "see ..." if it does not.
-
- def see
- @see = find_see if @see == false
- @see
- end
+ def initialize_copy(other) # :nodoc:
+ @full_name = nil
+ end
- ##
- # Sets the store for this class or module and its contained code objects.
+ def initialize_visibility # :nodoc:
+ super
+ @see = nil
+ end
- def store=(store)
- super
+ ##
+ # Order by #singleton then #name
- @file = @store.add_file @file.full_name if @file
- end
+ def <=>(other)
+ return unless other.respond_to?(:singleton) &&
+ other.respond_to?(:name)
- def find_see # :nodoc:
- return nil if singleton || is_alias_for
+ [@singleton ? 0 : 1, name_ord_range, name] <=>
+ [other.singleton ? 0 : 1, other.name_ord_range, other.name]
+ end
- # look for the method
- other = find_method_or_attribute name
- return other if other
+ def ==(other) # :nodoc:
+ equal?(other) or self.class == other.class and full_name == other.full_name
+ end
- # if it is a setter, look for a getter
- return nil unless name =~ /[a-z_]=$/i # avoid == or ===
- return find_method_or_attribute name[0..-2]
- end
+ ##
+ # A method/attribute is documented if any of the following is true:
+ # - it was marked with :nodoc:;
+ # - it has a comment;
+ # - it is an alias for a documented method;
+ # - it has a +#see+ method that is documented.
+
+ def documented?
+ super or
+ (is_alias_for and is_alias_for.documented?) or
+ (see and see.documented?)
+ end
- def find_method_or_attribute(name) # :nodoc:
- return nil unless parent.respond_to? :ancestors
+ ##
+ # A method/attribute to look at,
+ # in particular if this method/attribute has no documentation.
+ #
+ # It can be a method/attribute of the superclass or of an included module,
+ # including the Kernel module, which is always appended to the included
+ # modules.
+ #
+ # Returns +nil+ if there is no such method/attribute.
+ # The +#is_alias_for+ method/attribute, if any, is not included.
+ #
+ # Templates may generate a "see also ..." if this method/attribute
+ # has documentation, and "see ..." if it does not.
+
+ def see
+ @see = find_see if @see == false
+ @see
+ end
- searched = parent.ancestors
- kernel = @store.modules_hash['Kernel']
+ ##
+ # Sets the store for this class or module and its contained code objects.
- searched << kernel if kernel &&
- parent != kernel && !searched.include?(kernel)
+ def store=(store)
+ super
- searched.each do |ancestor|
- next if String === ancestor
- next if parent == ancestor
+ @file = @store.add_file @file.full_name if @file
+ end
- other = ancestor.find_method_named('#' + name) ||
- ancestor.find_attribute_named(name)
+ def find_see # :nodoc:
+ return nil if singleton || is_alias_for
+ # look for the method
+ other = find_method_or_attribute name
return other if other
+
+ # if it is a setter, look for a getter
+ return nil unless name =~ /[a-z_]=$/i # avoid == or ===
+ return find_method_or_attribute name[0..-2]
end
- nil
- end
+ def find_method_or_attribute(name) # :nodoc:
+ return nil unless parent.respond_to? :ancestors
- ##
- # Abstract method. Contexts in their building phase call this
- # to register a new alias for this known method/attribute.
- #
- # - creates a new AnyMethod/Attribute named an_alias.new_name;
- # - adds +self+ as an alias for the new method or attribute
- # - adds the method or attribute to #aliases
- # - adds the method or attribute to +context+.
-
- def add_alias(an_alias, context)
- raise NotImplementedError
- end
+ searched = parent.ancestors
+ kernel = @store.modules_hash['Kernel']
- ##
- # HTML fragment reference for this method
+ searched << kernel if kernel &&
+ parent != kernel && !searched.include?(kernel)
- def aref
- type = singleton ? 'c' : 'i'
- # % characters are not allowed in html names => dash instead
- "#{aref_prefix}-#{type}-#{html_name}"
- end
+ searched.each do |ancestor|
+ next if String === ancestor
+ next if parent == ancestor
- ##
- # Prefix for +aref+, defined by subclasses.
+ other = ancestor.find_method_named('#' + name) ||
+ ancestor.find_attribute_named(name)
- def aref_prefix
- raise NotImplementedError
- end
+ return other if other
+ end
- ##
- # Attempts to sanitize the content passed by the Ruby parser:
- # remove outer parentheses, etc.
+ nil
+ end
+
+ ##
+ # Abstract method. Contexts in their building phase call this
+ # to register a new alias for this known method/attribute.
+ #
+ # - creates a new AnyMethod/Attribute named an_alias.new_name;
+ # - adds +self+ as an alias for the new method or attribute
+ # - adds the method or attribute to #aliases
+ # - adds the method or attribute to +context+.
+
+ def add_alias(an_alias, context)
+ raise NotImplementedError
+ end
- def block_params=(value)
- # 'yield.to_s' or 'assert yield, msg'
- return @block_params = '' if value =~ /^[\.,]/
+ ##
+ # HTML fragment reference for this method
- # remove trailing 'if/unless ...'
- return @block_params = '' if value =~ /^(if|unless)\s/
+ def aref
+ type = singleton ? 'c' : 'i'
+ # % characters are not allowed in html names => dash instead
+ "#{aref_prefix}-#{type}-#{html_name}"
+ end
- value = $1.strip if value =~ /^(.+)\s(if|unless)\s/
+ ##
+ # Prefix for +aref+, defined by subclasses.
- # outer parentheses
- value = $1 if value =~ /^\s*\((.*)\)\s*$/
- value = value.strip
+ def aref_prefix
+ raise NotImplementedError
+ end
- # proc/lambda
- return @block_params = $1 if value =~ /^(proc|lambda)(\s*\{|\sdo)/
+ ##
+ # Attempts to sanitize the content passed by the Ruby parser:
+ # remove outer parentheses, etc.
- # surrounding +...+ or [...]
- value = $1.strip if value =~ /^\+(.*)\+$/
- value = $1.strip if value =~ /^\[(.*)\]$/
+ def block_params=(value)
+ # 'yield.to_s' or 'assert yield, msg'
+ return @block_params = '' if value =~ /^[\.,]/
- return @block_params = '' if value.empty?
+ # remove trailing 'if/unless ...'
+ return @block_params = '' if value =~ /^(if|unless)\s/
- # global variable
- return @block_params = 'str' if value =~ /^\$[&0-9]$/
+ value = $1.strip if value =~ /^(.+)\s(if|unless)\s/
- # wipe out array/hash indices
- value.gsub!(/(\w)\[[^\[]+\]/, '\1')
+ # outer parentheses
+ value = $1 if value =~ /^\s*\((.*)\)\s*$/
+ value = value.strip
- # remove @ from class/instance variables
- value.gsub!(/@@?([a-z0-9_]+)/, '\1')
+ # proc/lambda
+ return @block_params = $1 if value =~ /^(proc|lambda)(\s*\{|\sdo)/
- # method calls => method name
- value.gsub!(/([A-Z:a-z0-9_]+)\.([a-z0-9_]+)(\s*\(\s*[a-z0-9_.,\s]*\s*\)\s*)?/) do
- case $2
- when 'to_s' then $1
- when 'const_get' then 'const'
- when 'new'
- $1.split('::').last. # ClassName => class_name
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
- gsub(/([a-z\d])([A-Z])/, '\1_\2').
- downcase
- else
- $2
- end
- end
+ # surrounding +...+ or [...]
+ value = $1.strip if value =~ /^\+(.*)\+$/
+ value = $1.strip if value =~ /^\[(.*)\]$/
- # class prefixes
- value.gsub!(/[A-Za-z0-9_:]+::/, '')
+ return @block_params = '' if value.empty?
- # simple expressions
- value = $1 if value =~ /^([a-z0-9_]+)\s*[-*+\/]/
+ # global variable
+ return @block_params = 'str' if value =~ /^\$[&0-9]$/
- @block_params = value.strip
- end
+ # wipe out array/hash indices
+ value.gsub!(/(\w)\[[^\[]+\]/, '\1')
- ##
- # HTML id-friendly method/attribute name
+ # remove @ from class/instance variables
+ value.gsub!(/@@?([a-z0-9_]+)/, '\1')
- def html_name
- require 'cgi/escape'
- require 'cgi/util' unless defined?(CGI::EscapeExt)
+ # method calls => method name
+ value.gsub!(/([A-Z:a-z0-9_]+)\.([a-z0-9_]+)(\s*\(\s*[a-z0-9_.,\s]*\s*\)\s*)?/) do
+ case $2
+ when 'to_s' then $1
+ when 'const_get' then 'const'
+ when 'new'
+ $1.split('::').last. # ClassName => class_name
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
+ downcase
+ else
+ $2
+ end
+ end
- CGI.escape(@name.gsub('-', '-2D')).gsub('%', '-').sub(/^-/, '')
- end
+ # class prefixes
+ value.gsub!(/[A-Za-z0-9_:]+::/, '')
- ##
- # Full method/attribute name including namespace
+ # simple expressions
+ value = $1 if value =~ /^([a-z0-9_]+)\s*[-*+\/]/
- def full_name
- @full_name ||= "#{parent_name}#{pretty_name}"
- end
+ @block_params = value.strip
+ end
- def inspect # :nodoc:
- alias_for =
- if @is_alias_for.respond_to? :name
- " (alias for #{@is_alias_for.name})"
- elsif Array === @is_alias_for
- " (alias for #{@is_alias_for.last})"
- end
- visibility = self.visibility
- visibility = "forced #{visibility}" if force_documentation
- "#<%s:0x%x %s (%s)%s>" % [
- self.class, object_id,
- full_name,
- visibility,
- alias_for,
- ]
- end
+ ##
+ # HTML id-friendly method/attribute name
- ##
- # '::' for a class method/attribute, '#' for an instance method.
+ def html_name
+ require 'cgi/escape'
+ require 'cgi/util' unless defined?(CGI::EscapeExt)
- def name_prefix
- @singleton ? '::' : '#'
- end
+ CGI.escape(@name.gsub('-', '-2D')).gsub('%', '-').sub(/^-/, '')
+ end
- ##
- # Method/attribute name with class/instance indicator
+ ##
+ # Full method/attribute name including namespace
- def pretty_name
- "#{name_prefix}#{@name}"
- end
+ def full_name
+ @full_name ||= "#{parent_name}#{pretty_name}"
+ end
- ##
- # Type of method/attribute (class or instance)
+ def inspect # :nodoc:
+ alias_for =
+ if @is_alias_for.respond_to? :name
+ " (alias for #{@is_alias_for.name})"
+ elsif Array === @is_alias_for
+ " (alias for #{@is_alias_for.last})"
+ end
+ visibility = self.visibility
+ visibility = "forced #{visibility}" if force_documentation
+ "#<%s:0x%x %s (%s)%s>" % [
+ self.class, object_id,
+ full_name,
+ visibility,
+ alias_for,
+ ]
+ end
- def type
- singleton ? 'class' : 'instance'
- end
+ ##
+ # '::' for a class method/attribute, '#' for an instance method.
- ##
- # Path to this method for use with HTML generator output.
+ def name_prefix
+ @singleton ? '::' : '#'
+ end
- def path
- "#{@parent.path}##{aref}"
- end
+ ##
+ # Method/attribute name with class/instance indicator
- ##
- # Name of our parent with special handling for un-marshaled methods
+ def pretty_name
+ "#{name_prefix}#{@name}"
+ end
- def parent_name
- @parent_name || super
- end
+ ##
+ # Type of method/attribute (class or instance)
- def pretty_print(q) # :nodoc:
- alias_for =
- if @is_alias_for.respond_to? :name
- "alias for #{@is_alias_for.name}"
- elsif Array === @is_alias_for
- "alias for #{@is_alias_for.last}"
- end
+ def type
+ singleton ? 'class' : 'instance'
+ end
- q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
- if alias_for
- q.breakable
- q.text alias_for
- end
+ ##
+ # Path to this method for use with HTML generator output.
+
+ def path
+ "#{@parent.path}##{aref}"
+ end
+
+ ##
+ # Name of our parent with special handling for un-marshaled methods
+
+ def parent_name
+ @parent_name || super
+ end
- unless comment.empty?
- q.breakable
- q.text "comment:"
- q.breakable
- q.pp @comment
+ def pretty_print(q) # :nodoc:
+ alias_for =
+ if @is_alias_for.respond_to? :name
+ "alias for #{@is_alias_for.name}"
+ elsif Array === @is_alias_for
+ "alias for #{@is_alias_for.last}"
+ end
+
+ q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
+ if alias_for
+ q.breakable
+ q.text alias_for
+ end
+
+ unless comment.empty?
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
end
end
- end
- ##
- # Used by RDoc::Generator::JsonIndex to create a record for the search
- # engine.
- #
- # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
- # Use #search_snippet instead for getting documentation snippets.
-
- def search_record
- [
- @name,
- full_name,
- @name,
- @parent.full_name,
- path,
- params,
- search_snippet,
- ]
- end
+ ##
+ # Used by RDoc::Generator::JsonIndex to create a record for the search
+ # engine.
+ #
+ # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
+ # Use #search_snippet instead for getting documentation snippets.
+
+ def search_record
+ [
+ @name,
+ full_name,
+ @name,
+ @parent.full_name,
+ path,
+ params,
+ search_snippet,
+ ]
+ end
- ##
- # Returns an HTML snippet of the comment for search results.
+ ##
+ # Returns an HTML snippet of the comment for search results.
- def search_snippet
- return '' if @comment.empty?
+ def search_snippet
+ return '' if @comment.empty?
- snippet(@comment)
- end
+ snippet(@comment)
+ end
- def to_s # :nodoc:
- if @is_alias_for
- "#{self.class.name}: #{full_name} -> #{is_alias_for}"
- else
- "#{self.class.name}: #{full_name}"
+ def to_s # :nodoc:
+ if @is_alias_for
+ "#{self.class.name}: #{full_name} -> #{is_alias_for}"
+ else
+ "#{self.class.name}: #{full_name}"
+ end
end
- end
- def name_ord_range # :nodoc:
- case name.ord
- when 0..64 # anything below "A"
- 1
- when 91..96 # the symbols between "Z" and "a"
- 2
- when 123..126 # 7-bit symbols above "z": "{", "|", "}", "~"
- 3
- else # everythig else can be sorted as normal
- 4
+ def name_ord_range # :nodoc:
+ case name.ord
+ when 0..64 # anything below "A"
+ 1
+ when 91..96 # the symbols between "Z" and "a"
+ 2
+ when 123..126 # 7-bit symbols above "z": "{", "|", "}", "~"
+ 3
+ else # everythig else can be sorted as normal
+ 4
+ end
end
end
end
diff --git a/lib/rdoc/code_object/mixin.rb b/lib/rdoc/code_object/mixin.rb
index 39c8311b2f..0a4d8f8bb4 100644
--- a/lib/rdoc/code_object/mixin.rb
+++ b/lib/rdoc/code_object/mixin.rb
@@ -1,123 +1,125 @@
# frozen_string_literal: true
-##
-# A Mixin adds features from a module into another context. RDoc::Include and
-# RDoc::Extend are both mixins.
+module RDoc
+ ##
+ # A Mixin adds features from a module into another context. RDoc::Include and
+ # RDoc::Extend are both mixins.
-class RDoc::Mixin < RDoc::CodeObject
+ class Mixin < CodeObject
- ##
- # Name of included module
+ ##
+ # Name of included module
- attr_accessor :name
+ attr_accessor :name
- ##
- # Creates a new Mixin for +name+ with +comment+
+ ##
+ # Creates a new Mixin for +name+ with +comment+
- def initialize(name, comment)
- super()
- @name = name
- self.comment = comment
- @module = nil # cache for module if found
- end
+ def initialize(name, comment)
+ super()
+ @name = name
+ self.comment = comment
+ @module = nil # cache for module if found
+ end
- ##
- # Mixins are sorted by name
+ ##
+ # Mixins are sorted by name
- def <=>(other)
- return unless self.class === other
+ def <=>(other)
+ return unless self.class === other
- name <=> other.name
- end
+ name <=> other.name
+ end
- def ==(other) # :nodoc:
- self.class === other and @name == other.name
- end
+ def ==(other) # :nodoc:
+ self.class === other and @name == other.name
+ end
- alias eql? == # :nodoc:
+ alias eql? == # :nodoc:
- ##
- # Full name based on #module
+ ##
+ # Full name based on #module
- def full_name
- m = self.module
- RDoc::ClassModule === m ? m.full_name : @name
- end
+ def full_name
+ m = self.module
+ ClassModule === m ? m.full_name : @name
+ end
- def hash # :nodoc:
- [@name, self.module].hash
- end
+ def hash # :nodoc:
+ [@name, self.module].hash
+ end
- def inspect # :nodoc:
- "#<%s:0x%x %s.%s %s>" % [
- self.class,
- object_id,
- parent_name, self.class.name.downcase, @name,
- ]
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x %s.%s %s>" % [
+ self.class,
+ object_id,
+ parent_name, self.class.name.downcase, @name,
+ ]
+ end
- ##
- # Attempts to locate the included module object. Returns the name if not
- # known.
- #
- # The scoping rules of Ruby to resolve the name of an included module are:
- # - first look into the children of the current context;
- # - if not found, look into the children of included modules,
- # in reverse inclusion order;
- # - if still not found, go up the hierarchy of names.
- #
- # This method has O(n!) behavior when the module calling
- # include is referencing nonexistent modules. Avoid calling #module until
- # after all the files are parsed. This behavior is due to ruby's constant
- # lookup behavior.
- #
- # As of the beginning of October, 2011, no gem includes nonexistent modules.
- #
- # The Ruby parser passes an already-resolved full-path +name+, so most of this
- # logic only runs for the C parser, which passes the unresolved local name.
-
- def module
- return @module if @module
-
- # search the current context
- return @name unless parent
- full_name = parent.child_name(@name)
- @module = @store.modules_hash[full_name]
- return @module if @module
- return @name if @name =~ /^::/
-
- # search the includes before this one, in reverse order
- searched = parent.includes.take_while { |i| i != self }.reverse
- searched.each do |i|
- inc = i.module
- next if String === inc
- full_name = inc.child_name(@name)
- @module = @store.modules_hash[full_name]
+ ##
+ # Attempts to locate the included module object. Returns the name if not
+ # known.
+ #
+ # The scoping rules of Ruby to resolve the name of an included module are:
+ # - first look into the children of the current context;
+ # - if not found, look into the children of included modules,
+ # in reverse inclusion order;
+ # - if still not found, go up the hierarchy of names.
+ #
+ # This method has O(n!) behavior when the module calling
+ # include is referencing nonexistent modules. Avoid calling #module until
+ # after all the files are parsed. This behavior is due to ruby's constant
+ # lookup behavior.
+ #
+ # As of the beginning of October, 2011, no gem includes nonexistent modules.
+ #
+ # The Ruby parser passes an already-resolved full-path +name+, so most of this
+ # logic only runs for the C parser, which passes the unresolved local name.
+
+ def module
return @module if @module
- end
- # go up the hierarchy of names
- up = parent.parent
- while up
- full_name = up.child_name(@name)
+ # search the current context
+ return @name unless parent
+ full_name = parent.child_name(@name)
@module = @store.modules_hash[full_name]
return @module if @module
- up = up.parent
+ return @name if @name =~ /^::/
+
+ # search the includes before this one, in reverse order
+ searched = parent.includes.take_while { |i| i != self }.reverse
+ searched.each do |i|
+ inc = i.module
+ next if String === inc
+ full_name = inc.child_name(@name)
+ @module = @store.modules_hash[full_name]
+ return @module if @module
+ end
+
+ # go up the hierarchy of names
+ up = parent.parent
+ while up
+ full_name = up.child_name(@name)
+ @module = @store.modules_hash[full_name]
+ return @module if @module
+ up = up.parent
+ end
+
+ @name
end
- @name
- end
+ ##
+ # Sets the store for this class or module and its contained code objects.
- ##
- # Sets the store for this class or module and its contained code objects.
+ def store=(store)
+ super
- def store=(store)
- super
+ @file = @store.add_file @file.full_name if @file
+ end
- @file = @store.add_file @file.full_name if @file
- end
+ def to_s # :nodoc:
+ "#{self.class.name.downcase} #@name in: #{parent}"
+ end
- def to_s # :nodoc:
- "#{self.class.name.downcase} #@name in: #{parent}"
end
-
end
diff --git a/lib/rdoc/code_object/normal_class.rb b/lib/rdoc/code_object/normal_class.rb
index 6f648ea339..b8da1a7258 100644
--- a/lib/rdoc/code_object/normal_class.rb
+++ b/lib/rdoc/code_object/normal_class.rb
@@ -1,89 +1,91 @@
# frozen_string_literal: true
-##
-# A normal class, neither singleton nor anonymous
-
-class RDoc::NormalClass < RDoc::ClassModule
-
+module RDoc
##
- # The ancestors of this class including modules. Unlike Module#ancestors,
- # this class is not included in the result. The result will contain both
- # RDoc::ClassModules and Strings.
-
- def ancestors
- ancestors = included_ancestors
- super_classes.each do |sclass|
- ancestors << sclass
- ancestors.concat sclass.included_ancestors unless String === sclass
+ # A normal class, neither singleton nor anonymous
+
+ class NormalClass < ClassModule
+
+ ##
+ # The ancestors of this class including modules. Unlike Module#ancestors,
+ # this class is not included in the result. The result will contain both
+ # RDoc::ClassModules and Strings.
+
+ def ancestors
+ ancestors = included_ancestors
+ super_classes.each do |sclass|
+ ancestors << sclass
+ ancestors.concat sclass.included_ancestors unless String === sclass
+ end
+ ancestors
end
- ancestors
- end
- def aref_prefix # :nodoc:
- 'class'
- end
+ def aref_prefix # :nodoc:
+ 'class'
+ end
- ##
- # The definition of this class, class MyClassName
+ ##
+ # The definition of this class, class MyClassName
- def definition
- "class #{full_name}"
- end
+ def definition
+ "class #{full_name}"
+ end
- def direct_ancestors
- superclass ? super + [superclass] : super
- end
+ def direct_ancestors
+ superclass ? super + [superclass] : super
+ end
- def inspect # :nodoc:
- superclass = @superclass ? " < #{@superclass}" : nil
- "<%s:0x%x class %s%s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
- self.class, object_id,
- full_name, superclass, @includes, @extends, @attributes, @method_list, @aliases
- ]
- end
+ def inspect # :nodoc:
+ superclass = @superclass ? " < #{@superclass}" : nil
+ "<%s:0x%x class %s%s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ full_name, superclass, @includes, @extends, @attributes, @method_list, @aliases
+ ]
+ end
- def to_s # :nodoc:
- display = "#{self.class.name} #{self.full_name}"
- if superclass
- display += ' < ' + (superclass.is_a?(String) ? superclass : superclass.full_name)
+ def to_s # :nodoc:
+ display = "#{self.class.name} #{self.full_name}"
+ if superclass
+ display += ' < ' + (superclass.is_a?(String) ? superclass : superclass.full_name)
+ end
+ display += ' -> ' + is_alias_for.to_s if is_alias_for
+ display
end
- display += ' -> ' + is_alias_for.to_s if is_alias_for
- display
- end
- def pretty_print(q) # :nodoc:
- superclass = @superclass ? " < #{@superclass}" : nil
-
- q.group 2, "[class #{full_name}#{superclass}", "]" do
- q.breakable
- q.text "includes:"
- q.breakable
- q.seplist @includes do |inc| q.pp inc end
-
- q.breakable
- q.text "constants:"
- q.breakable
- q.seplist @constants do |const| q.pp const end
-
- q.breakable
- q.text "attributes:"
- q.breakable
- q.seplist @attributes do |attr| q.pp attr end
-
- q.breakable
- q.text "methods:"
- q.breakable
- q.seplist @method_list do |meth| q.pp meth end
-
- q.breakable
- q.text "aliases:"
- q.breakable
- q.seplist @aliases do |aliaz| q.pp aliaz end
-
- q.breakable
- q.text "comment:"
- q.breakable
- q.pp comment
+ def pretty_print(q) # :nodoc:
+ superclass = @superclass ? " < #{@superclass}" : nil
+
+ q.group 2, "[class #{full_name}#{superclass}", "]" do
+ q.breakable
+ q.text "includes:"
+ q.breakable
+ q.seplist @includes do |inc| q.pp inc end
+
+ q.breakable
+ q.text "constants:"
+ q.breakable
+ q.seplist @constants do |const| q.pp const end
+
+ q.breakable
+ q.text "attributes:"
+ q.breakable
+ q.seplist @attributes do |attr| q.pp attr end
+
+ q.breakable
+ q.text "methods:"
+ q.breakable
+ q.seplist @method_list do |meth| q.pp meth end
+
+ q.breakable
+ q.text "aliases:"
+ q.breakable
+ q.seplist @aliases do |aliaz| q.pp aliaz end
+
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp comment
+ end
end
- end
+ end
end
diff --git a/lib/rdoc/code_object/normal_module.rb b/lib/rdoc/code_object/normal_module.rb
index 677a9dc3bd..956df492b1 100644
--- a/lib/rdoc/code_object/normal_module.rb
+++ b/lib/rdoc/code_object/normal_module.rb
@@ -1,73 +1,75 @@
# frozen_string_literal: true
-##
-# A normal module, like NormalClass
+module RDoc
+ ##
+ # A normal module, like NormalClass
-class RDoc::NormalModule < RDoc::ClassModule
+ class NormalModule < ClassModule
- def aref_prefix # :nodoc:
- 'module'
- end
+ def aref_prefix # :nodoc:
+ 'module'
+ end
- def inspect # :nodoc:
- "#<%s:0x%x module %s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
- self.class, object_id,
- full_name, @includes, @extends, @attributes, @method_list, @aliases
- ]
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x module %s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ full_name, @includes, @extends, @attributes, @method_list, @aliases
+ ]
+ end
- ##
- # The definition of this module, module MyModuleName
+ ##
+ # The definition of this module, module MyModuleName
- def definition
- "module #{full_name}"
- end
+ def definition
+ "module #{full_name}"
+ end
- ##
- # This is a module, returns true
+ ##
+ # This is a module, returns true
- def module?
- true
- end
+ def module?
+ true
+ end
- def pretty_print(q) # :nodoc:
- q.group 2, "[module #{full_name}:", "]" do
- q.breakable
- q.text "includes:"
- q.breakable
- q.seplist @includes do |inc| q.pp inc end
- q.breakable
-
- q.breakable
- q.text "constants:"
- q.breakable
- q.seplist @constants do |const| q.pp const end
-
- q.text "attributes:"
- q.breakable
- q.seplist @attributes do |attr| q.pp attr end
- q.breakable
-
- q.text "methods:"
- q.breakable
- q.seplist @method_list do |meth| q.pp meth end
- q.breakable
-
- q.text "aliases:"
- q.breakable
- q.seplist @aliases do |aliaz| q.pp aliaz end
- q.breakable
-
- q.text "comment:"
- q.breakable
- q.pp comment
+ def pretty_print(q) # :nodoc:
+ q.group 2, "[module #{full_name}:", "]" do
+ q.breakable
+ q.text "includes:"
+ q.breakable
+ q.seplist @includes do |inc| q.pp inc end
+ q.breakable
+
+ q.breakable
+ q.text "constants:"
+ q.breakable
+ q.seplist @constants do |const| q.pp const end
+
+ q.text "attributes:"
+ q.breakable
+ q.seplist @attributes do |attr| q.pp attr end
+ q.breakable
+
+ q.text "methods:"
+ q.breakable
+ q.seplist @method_list do |meth| q.pp meth end
+ q.breakable
+
+ q.text "aliases:"
+ q.breakable
+ q.seplist @aliases do |aliaz| q.pp aliaz end
+ q.breakable
+
+ q.text "comment:"
+ q.breakable
+ q.pp comment
+ end
end
- end
- ##
- # Modules don't have one, raises NoMethodError
+ ##
+ # Modules don't have one, raises NoMethodError
- def superclass
- raise NoMethodError, "#{full_name} is a module"
- end
+ def superclass
+ raise NoMethodError, "#{full_name} is a module"
+ end
+ end
end
diff --git a/lib/rdoc/code_object/require.rb b/lib/rdoc/code_object/require.rb
index d65cd961ba..ed8f595ddd 100644
--- a/lib/rdoc/code_object/require.rb
+++ b/lib/rdoc/code_object/require.rb
@@ -1,51 +1,53 @@
# frozen_string_literal: true
-##
-# A file loaded by \#require
+module RDoc
+ ##
+ # A file loaded by \#require
-class RDoc::Require < RDoc::CodeObject
+ class Require < CodeObject
- ##
- # Name of the required file
+ ##
+ # Name of the required file
- attr_accessor :name
+ attr_accessor :name
- ##
- # Creates a new Require that loads +name+ with +comment+
+ ##
+ # Creates a new Require that loads +name+ with +comment+
- def initialize(name, comment)
- super()
- @name = name.gsub(/'|"/, "") #'
- @top_level = nil
- self.comment = comment
- end
+ def initialize(name, comment)
+ super()
+ @name = name.gsub(/'|"/, "") #'
+ @top_level = nil
+ self.comment = comment
+ end
- def inspect # :nodoc:
- "#<%s:0x%x require '%s' in %s>" % [
- self.class,
- object_id,
- @name,
- @parent ? @parent.base_name : '(unknown)'
- ]
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x require '%s' in %s>" % [
+ self.class,
+ object_id,
+ @name,
+ @parent ? @parent.base_name : '(unknown)'
+ ]
+ end
- def to_s # :nodoc:
- "require #{name} in: #{parent}"
- end
+ def to_s # :nodoc:
+ "require #{name} in: #{parent}"
+ end
- ##
- # The RDoc::TopLevel corresponding to this require, or +nil+ if not found.
+ ##
+ # The RDoc::TopLevel corresponding to this require, or +nil+ if not found.
- def top_level
- @top_level ||= begin
- tl = RDoc::TopLevel.all_files_hash[name + '.rb']
+ def top_level
+ @top_level ||= begin
+ tl = TopLevel.all_files_hash[name + '.rb']
- if tl.nil? and RDoc::TopLevel.all_files.first.full_name =~ %r(^lib/)
- # second chance
- tl = RDoc::TopLevel.all_files_hash['lib/' + name + '.rb']
- end
+ if tl.nil? and TopLevel.all_files.first.full_name =~ %r(^lib/)
+ # second chance
+ tl = TopLevel.all_files_hash['lib/' + name + '.rb']
+ end
- tl
+ tl
+ end
end
- end
+ end
end
diff --git a/lib/rdoc/code_object/single_class.rb b/lib/rdoc/code_object/single_class.rb
index 88a93a0c5f..273a39b0e3 100644
--- a/lib/rdoc/code_object/single_class.rb
+++ b/lib/rdoc/code_object/single_class.rb
@@ -1,30 +1,32 @@
# frozen_string_literal: true
-##
-# A singleton class
+module RDoc
+ ##
+ # A singleton class
-class RDoc::SingleClass < RDoc::ClassModule
+ class SingleClass < ClassModule
- ##
- # Adds the superclass to the included modules.
+ ##
+ # Adds the superclass to the included modules.
- def ancestors
- superclass ? super + [superclass] : super
- end
+ def ancestors
+ superclass ? super + [superclass] : super
+ end
- def aref_prefix # :nodoc:
- 'sclass'
- end
+ def aref_prefix # :nodoc:
+ 'sclass'
+ end
- ##
- # The definition of this singleton class, class << MyClassName
+ ##
+ # The definition of this singleton class, class << MyClassName
- def definition
- "class << #{full_name}"
- end
+ def definition
+ "class << #{full_name}"
+ end
- def pretty_print(q) # :nodoc:
- q.group 2, "[class << #{full_name}", "]" do
- next
+ def pretty_print(q) # :nodoc:
+ q.group 2, "[class << #{full_name}", "]" do
+ next
+ end
end
end
end
diff --git a/lib/rdoc/code_object/top_level.rb b/lib/rdoc/code_object/top_level.rb
index 0cc98c0469..3f5e2d0822 100644
--- a/lib/rdoc/code_object/top_level.rb
+++ b/lib/rdoc/code_object/top_level.rb
@@ -1,286 +1,288 @@
# frozen_string_literal: true
-##
-# A TopLevel context is a representation of the contents of a single file
+module RDoc
+ ##
+ # A TopLevel context is a representation of the contents of a single file
-class RDoc::TopLevel < RDoc::Context
+ class TopLevel < Context
- MARSHAL_VERSION = 0 # :nodoc:
+ MARSHAL_VERSION = 0 # :nodoc:
- ##
- # Relative name of this file
+ ##
+ # Relative name of this file
- attr_accessor :relative_name
+ attr_accessor :relative_name
- ##
- # Absolute name of this file
+ ##
+ # Absolute name of this file
- attr_accessor :absolute_name
+ attr_accessor :absolute_name
- ##
- # Base name of this file
+ ##
+ # Base name of this file
- attr_reader :base_name
+ attr_reader :base_name
- ##
- # Base name of this file without the extension
+ ##
+ # Base name of this file without the extension
- attr_reader :page_name
+ attr_reader :page_name
- ##
- # All the classes or modules that were declared in
- # this file. These are assigned to either +#classes_hash+
- # or +#modules_hash+ once we know what they really are.
+ ##
+ # All the classes or modules that were declared in
+ # this file. These are assigned to either +#classes_hash+
+ # or +#modules_hash+ once we know what they really are.
- attr_reader :classes_or_modules
+ attr_reader :classes_or_modules
- ##
- # The parser class that processed this file
+ ##
+ # The parser class that processed this file
- attr_reader :parser
+ attr_reader :parser
- ##
- # Creates a new TopLevel for the file at +absolute_name+. If documentation
- # is being generated outside the source dir +relative_name+ is relative to
- # the source directory.
-
- def initialize(absolute_name, relative_name = absolute_name)
- super()
- @name = nil
- @absolute_name = absolute_name
- @relative_name = relative_name
- @parser = nil
-
- if relative_name
- @base_name = File.basename(relative_name)
- @page_name = @base_name.sub(/\.(rb|rdoc|txt|md)\z/i, '')
- else
- @base_name = nil
- @page_name = nil
+ ##
+ # Creates a new TopLevel for the file at +absolute_name+. If documentation
+ # is being generated outside the source dir +relative_name+ is relative to
+ # the source directory.
+
+ def initialize(absolute_name, relative_name = absolute_name)
+ super()
+ @name = nil
+ @absolute_name = absolute_name
+ @relative_name = relative_name
+ @parser = nil
+
+ if relative_name
+ @base_name = File.basename(relative_name)
+ @page_name = @base_name.sub(/\.(rb|rdoc|txt|md)\z/i, '')
+ else
+ @base_name = nil
+ @page_name = nil
+ end
+
+ @classes_or_modules = []
end
- @classes_or_modules = []
- end
+ ##
+ # Sets the parser for this toplevel context, also the store.
- ##
- # Sets the parser for this toplevel context, also the store.
+ def parser=(val)
+ @parser = val
+ @store&.cache_text_file(relative_name)
+ @parser
+ end
- def parser=(val)
- @parser = val
- @store&.cache_text_file(relative_name)
- @parser
- end
+ ##
+ # An RDoc::TopLevel is equal to another with the same relative_name
- ##
- # An RDoc::TopLevel is equal to another with the same relative_name
+ def ==(other)
+ self.class === other and @relative_name == other.relative_name
+ end
- def ==(other)
- self.class === other and @relative_name == other.relative_name
- end
+ alias eql? ==
- alias eql? ==
+ ##
+ # Adds +an_alias+ to +Object+ instead of +self+.
- ##
- # Adds +an_alias+ to +Object+ instead of +self+.
+ def add_alias(an_alias)
+ object_class.record_location self
+ return an_alias unless @document_self
+ object_class.add_alias an_alias
+ end
- def add_alias(an_alias)
- object_class.record_location self
- return an_alias unless @document_self
- object_class.add_alias an_alias
- end
+ ##
+ # Adds +constant+ to +Object+ instead of +self+.
- ##
- # Adds +constant+ to +Object+ instead of +self+.
+ def add_constant(constant)
+ object_class.record_location self
+ return constant unless @document_self
+ object_class.add_constant constant
+ end
- def add_constant(constant)
- object_class.record_location self
- return constant unless @document_self
- object_class.add_constant constant
- end
+ ##
+ # Adds +include+ to +Object+ instead of +self+.
- ##
- # Adds +include+ to +Object+ instead of +self+.
+ def add_include(include)
+ object_class.record_location self
+ return include unless @document_self
+ object_class.add_include include
+ end
- def add_include(include)
- object_class.record_location self
- return include unless @document_self
- object_class.add_include include
- end
+ ##
+ # Adds +method+ to +Object+ instead of +self+.
- ##
- # Adds +method+ to +Object+ instead of +self+.
+ def add_method(method)
+ object_class.record_location self
+ return method unless @document_self
+ object_class.add_method method
+ end
- def add_method(method)
- object_class.record_location self
- return method unless @document_self
- object_class.add_method method
- end
+ ##
+ # Adds class or module +mod+. Used in the building phase
+ # by the Ruby parser.
- ##
- # Adds class or module +mod+. Used in the building phase
- # by the Ruby parser.
+ def add_to_classes_or_modules(mod)
+ @classes_or_modules << mod
+ end
- def add_to_classes_or_modules(mod)
- @classes_or_modules << mod
- end
+ alias name base_name
- alias name base_name
+ ##
+ # See RDoc::TopLevel::find_class_or_module
+ #--
+ # TODO Why do we search through all classes/modules found, not just the
+ # ones of this instance?
- ##
- # See RDoc::TopLevel::find_class_or_module
- #--
- # TODO Why do we search through all classes/modules found, not just the
- # ones of this instance?
+ def find_class_or_module(name)
+ @store.find_class_or_module name
+ end
- def find_class_or_module(name)
- @store.find_class_or_module name
- end
+ ##
+ # Finds a class or module named +symbol+
- ##
- # Finds a class or module named +symbol+
+ def find_local_symbol(symbol)
+ find_class_or_module(symbol) || super
+ end
- def find_local_symbol(symbol)
- find_class_or_module(symbol) || super
- end
+ ##
+ # Finds a module or class with +name+
- ##
- # Finds a module or class with +name+
+ def find_module_named(name)
+ find_class_or_module(name)
+ end
- def find_module_named(name)
- find_class_or_module(name)
- end
+ alias get_module_named find_module_named
- alias get_module_named find_module_named
+ ##
+ # Returns the relative name of this file
- ##
- # Returns the relative name of this file
+ def full_name
+ @relative_name
+ end
- def full_name
- @relative_name
- end
+ ##
+ # An RDoc::TopLevel has the same hash as another with the same
+ # relative_name
- ##
- # An RDoc::TopLevel has the same hash as another with the same
- # relative_name
+ def hash
+ @relative_name.hash
+ end
- def hash
- @relative_name.hash
- end
+ ##
+ # URL for this with a +prefix+
- ##
- # URL for this with a +prefix+
+ def http_url
+ @relative_name.tr('.', '_') + '.html'
+ end
- def http_url
- @relative_name.tr('.', '_') + '.html'
- end
+ def inspect # :nodoc:
+ "#<%s:0x%x %p modules: %p classes: %p>" % [
+ self.class, object_id,
+ base_name,
+ @modules.map { |n, m| m },
+ @classes.map { |n, c| c }
+ ]
+ end
- def inspect # :nodoc:
- "#<%s:0x%x %p modules: %p classes: %p>" % [
- self.class, object_id,
- base_name,
- @modules.map { |n, m| m },
- @classes.map { |n, c| c }
- ]
- end
+ ##
+ # Dumps this TopLevel for use by ri. See also #marshal_load
- ##
- # Dumps this TopLevel for use by ri. See also #marshal_load
-
- def marshal_dump
- [
- MARSHAL_VERSION,
- @relative_name,
- @parser,
- parse(@comment),
- ]
- end
+ def marshal_dump
+ [
+ MARSHAL_VERSION,
+ @relative_name,
+ @parser,
+ parse(@comment),
+ ]
+ end
- ##
- # Loads this TopLevel from +array+.
+ ##
+ # Loads this TopLevel from +array+.
- def marshal_load(array) # :nodoc:
- initialize array[1]
+ def marshal_load(array) # :nodoc:
+ initialize array[1]
- @parser = array[2]
- @comment = RDoc::Comment.from_document array[3]
- end
+ @parser = array[2]
+ @comment = Comment.from_document array[3]
+ end
- ##
- # Returns the NormalClass "Object", creating it if not found.
- #
- # Records +self+ as a location in "Object".
-
- def object_class
- @object_class ||= begin
- oc = @store.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
- oc.record_location self
- oc
+ ##
+ # Returns the NormalClass "Object", creating it if not found.
+ #
+ # Records +self+ as a location in "Object".
+
+ def object_class
+ @object_class ||= begin
+ oc = @store.find_class_named('Object') || add_class(NormalClass, 'Object')
+ oc.record_location self
+ oc
+ end
end
- end
- ##
- # Path to this file for use with HTML generator output.
-
- def path
- base = if options.main_page == full_name
- 'index.html'
- else
- http_url
- end
-
- prefix = options.file_path_prefix
- return base unless prefix
- File.join(prefix, base)
- end
+ ##
+ # Path to this file for use with HTML generator output.
- def pretty_print(q) # :nodoc:
- q.group 2, "[#{self.class}: ", "]" do
- q.text "base name: #{base_name.inspect}"
- q.breakable
+ def path
+ base = if options.main_page == full_name
+ 'index.html'
+ else
+ http_url
+ end
- items = @modules.map { |n, m| m }
- items.concat @modules.map { |n, c| c }
- q.seplist items do |mod| q.pp mod end
+ prefix = options.file_path_prefix
+ return base unless prefix
+ File.join(prefix, base)
end
- end
- ##
- # Search record used by RDoc::Generator::JsonIndex
- #
- # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
- # Use #search_snippet instead for getting documentation snippets.
-
- def search_record
- return unless @parser < RDoc::Parser::Text
-
- [
- page_name,
- '',
- page_name,
- '',
- path,
- '',
- search_snippet,
- ]
- end
+ def pretty_print(q) # :nodoc:
+ q.group 2, "[#{self.class}: ", "]" do
+ q.text "base name: #{base_name.inspect}"
+ q.breakable
- ##
- # Returns an HTML snippet of the comment for search results.
+ items = @modules.map { |n, m| m }
+ items.concat @modules.map { |n, c| c }
+ q.seplist items do |mod| q.pp mod end
+ end
+ end
- def search_snippet
- return '' if @comment.empty?
+ ##
+ # Search record used by RDoc::Generator::JsonIndex
+ #
+ # TODO: Remove this method after dropping the darkfish theme and JsonIndex generator.
+ # Use #search_snippet instead for getting documentation snippets.
+
+ def search_record
+ return unless @parser < Parser::Text
+
+ [
+ page_name,
+ '',
+ page_name,
+ '',
+ path,
+ '',
+ search_snippet,
+ ]
+ end
- snippet(@comment)
- end
+ ##
+ # Returns an HTML snippet of the comment for search results.
- ##
- # Is this TopLevel from a text file instead of a source code file?
+ def search_snippet
+ return '' if @comment.empty?
- def text?
- @parser and @parser.include? RDoc::Parser::Text
- end
+ snippet(@comment)
+ end
- def to_s # :nodoc:
- "file #{full_name}"
- end
+ ##
+ # Is this TopLevel from a text file instead of a source code file?
+ def text?
+ @parser and @parser.include? Parser::Text
+ end
+
+ def to_s # :nodoc:
+ "file #{full_name}"
+ end
+
+ end
end
diff --git a/lib/rdoc/comment.rb b/lib/rdoc/comment.rb
index b50193d56c..70b3741e08 100644
--- a/lib/rdoc/comment.rb
+++ b/lib/rdoc/comment.rb
@@ -1,352 +1,354 @@
# frozen_string_literal: true
-##
-# A comment holds the text comment for a RDoc::CodeObject and provides a
-# unified way of cleaning it up and parsing it into an RDoc::Markup::Document.
-#
-# Each comment may have a different markup format set by #format=. By default
-# 'rdoc' is used. The :markup: directive tells RDoc which format to use.
-#
-# See {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc@Directive+for+Specifying+RDoc+Source+Format].
+module RDoc
+ ##
+ # A comment holds the text comment for a RDoc::CodeObject and provides a
+ # unified way of cleaning it up and parsing it into an RDoc::Markup::Document.
+ #
+ # Each comment may have a different markup format set by #format=. By default
+ # 'rdoc' is used. The :markup: directive tells RDoc which format to use.
+ #
+ # See {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc@Directive+for+Specifying+RDoc+Source+Format].
-class RDoc::Comment
+ class Comment
- include RDoc::Text
+ include Text
- ##
- # The format of this comment. Defaults to RDoc::Markup
+ ##
+ # The format of this comment. Defaults to RDoc::Markup
- attr_reader :format
+ attr_reader :format
- ##
- # The RDoc::TopLevel this comment was found in
+ ##
+ # The RDoc::TopLevel this comment was found in
- attr_accessor :location
+ attr_accessor :location
- ##
- # Line where this Comment was written
+ ##
+ # Line where this Comment was written
- attr_accessor :line
+ attr_accessor :line
- ##
- # For duck-typing when merging classes at load time
+ ##
+ # For duck-typing when merging classes at load time
- alias file location # :nodoc:
+ alias file location # :nodoc:
- ##
- # The text for this comment
+ ##
+ # The text for this comment
- attr_reader :text
+ attr_reader :text
- ##
- # Alias for text
+ ##
+ # Alias for text
- alias to_s text
+ alias to_s text
- ##
- # Overrides the content returned by #parse. Use when there is no #text
- # source for this comment
+ ##
+ # Overrides the content returned by #parse. Use when there is no #text
+ # source for this comment
- attr_writer :document
+ attr_writer :document
- ##
- # Creates a new comment with +text+ that is found in the RDoc::TopLevel
- # +location+.
-
- def initialize(text = nil, location = nil, language = nil)
- @location = location
- @text = text.nil? ? nil : text.dup
- @language = language
+ ##
+ # Creates a new comment with +text+ that is found in the RDoc::TopLevel
+ # +location+.
- @document = nil
- @format = 'rdoc'
- @normalized = false
- end
+ def initialize(text = nil, location = nil, language = nil)
+ @location = location
+ @text = text.nil? ? nil : text.dup
+ @language = language
- ##
- #--
- # TODO deep copy @document
+ @document = nil
+ @format = 'rdoc'
+ @normalized = false
+ end
- def initialize_copy(copy) # :nodoc:
- @text = copy.text.dup
- end
+ ##
+ #--
+ # TODO deep copy @document
- def ==(other) # :nodoc:
- self.class === other and
- other.text == @text and other.location == @location
- end
+ def initialize_copy(copy) # :nodoc:
+ @text = copy.text.dup
+ end
- ##
- # A comment is empty if its text String is empty.
+ def ==(other) # :nodoc:
+ self.class === other and
+ other.text == @text and other.location == @location
+ end
- def empty?
- @text.empty? && (@document.nil? || @document.empty?)
- end
+ ##
+ # A comment is empty if its text String is empty.
- ##
- # HACK dubious
+ def empty?
+ @text.empty? && (@document.nil? || @document.empty?)
+ end
- def encode!(encoding)
- @text = String.new @text, encoding: encoding
- self
- end
+ ##
+ # HACK dubious
- ##
- # Sets the format of this comment and resets any parsed document
+ def encode!(encoding)
+ @text = String.new @text, encoding: encoding
+ self
+ end
- def format=(format)
- @format = format
- @document = nil
- end
+ ##
+ # Sets the format of this comment and resets any parsed document
- def inspect # :nodoc:
- location = @location ? @location.relative_name : '(unknown)'
+ def format=(format)
+ @format = format
+ @document = nil
+ end
- "#<%s:%x %s %p>" % [self.class, object_id, location, @text]
- end
+ def inspect # :nodoc:
+ location = @location ? @location.relative_name : '(unknown)'
- ##
- # Normalizes the text. See RDoc::Text#normalize_comment for details
+ "#<%s:%x %s %p>" % [self.class, object_id, location, @text]
+ end
- def normalize
- return self unless @text
- return self if @normalized # TODO eliminate duplicate normalization
+ ##
+ # Normalizes the text. See RDoc::Text#normalize_comment for details
- @text = normalize_comment @text
+ def normalize
+ return self unless @text
+ return self if @normalized # TODO eliminate duplicate normalization
- @normalized = true
+ @text = normalize_comment @text
- self
- end
+ @normalized = true
- # Change normalized, when creating already normalized comment.
+ self
+ end
- def normalized=(value)
- @normalized = value
- end
+ # Change normalized, when creating already normalized comment.
- ##
- # Was this text normalized?
+ def normalized=(value)
+ @normalized = value
+ end
- def normalized? # :nodoc:
- @normalized
- end
+ ##
+ # Was this text normalized?
- ##
- # Parses the comment into an RDoc::Markup::Document. The parsed document is
- # cached until the text is changed.
+ def normalized? # :nodoc:
+ @normalized
+ end
- def parse
- return @document if @document
+ ##
+ # Parses the comment into an RDoc::Markup::Document. The parsed document is
+ # cached until the text is changed.
- @document = super @text, @format
- @document.file = @location
- @document
- end
+ def parse
+ return @document if @document
- ##
- # Replaces this comment's text with +text+ and resets the parsed document.
- #
- # An error is raised if the comment contains a document but no text.
+ @document = super @text, @format
+ @document.file = @location
+ @document
+ end
- def text=(text)
- raise RDoc::Error, 'replacing document-only comment is not allowed' if
- @text.nil? and @document
+ ##
+ # Replaces this comment's text with +text+ and resets the parsed document.
+ #
+ # An error is raised if the comment contains a document but no text.
- @document = nil
- @text = text.nil? ? nil : text.dup
- end
+ def text=(text)
+ raise Error, 'replacing document-only comment is not allowed' if
+ @text.nil? and @document
- ##
- # Returns true if this comment is in TomDoc format.
+ @document = nil
+ @text = text.nil? ? nil : text.dup
+ end
- def tomdoc?
- @format == 'tomdoc'
- end
+ ##
+ # Returns true if this comment is in TomDoc format.
- MULTILINE_DIRECTIVES = %w[call-seq].freeze # :nodoc:
+ def tomdoc?
+ @format == 'tomdoc'
+ end
- # There are more, but already handled by RDoc::Parser::C
- COLON_LESS_DIRECTIVES = %w[call-seq Document-method].freeze # :nodoc:
+ MULTILINE_DIRECTIVES = %w[call-seq].freeze # :nodoc:
- DIRECTIVE_OR_ESCAPED_DIRECTIV_REGEXP = /\A(?["'])(?.*?)\k .*\n - )+ - /xi # :nodoc: - +module RDoc ## - # Reads the contents of +filename+ and handles any encoding directives in - # the file. - # - # The content will be converted to the +encoding+. If the file cannot be - # converted a warning will be printed and nil will be returned. - # - # If +force_transcode+ is true the document will be transcoded and any - # unknown character in the target encoding will be replaced with '?' - - def self.read_file(filename, encoding, force_transcode = false) - content = File.open filename, "rb" do |f| f.read end - content.gsub!("\r\n", "\n") if RUBY_PLATFORM =~ /mswin|mingw/ - - utf8 = content.sub!(/\A\xef\xbb\xbf/, '') - - enc = RDoc::Encoding.detect_encoding content - content = RDoc::Encoding.change_encoding content, enc if enc - - begin - encoding ||= Encoding.default_external - orig_encoding = content.encoding - - if not orig_encoding.ascii_compatible? - content = content.encode encoding - elsif utf8 - content = RDoc::Encoding.change_encoding content, Encoding::UTF_8 - content = content.encode encoding - else - # assume the content is in our output encoding - content = RDoc::Encoding.change_encoding content, encoding - end - - unless content.valid_encoding? - # revert and try to transcode - content = RDoc::Encoding.change_encoding content, orig_encoding - content = content.encode encoding + # This class is a wrapper around File IO and Encoding that helps RDoc load + # files and convert them to the correct encoding. + + module Encoding + + HEADER_REGEXP = /\A + (?: + \#!.*\n + | + ^\#\s+frozen[-_]string[-_]literal[=:].+\n + | + ^\#\s*(?:-\*-\s*(?:[^;\n]*;\s*)*)?(?:en)?coding[=:]\s*(?[^:\s;]+).*\n + | + <\?xml[^?]*encoding=(? ["'])(?.*?)\k .*\n + )+ + /xi # :nodoc: + + ## + # Reads the contents of +filename+ and handles any encoding directives in + # the file. + # + # The content will be converted to the +encoding+. If the file cannot be + # converted a warning will be printed and nil will be returned. + # + # If +force_transcode+ is true the document will be transcoded and any + # unknown character in the target encoding will be replaced with '?' + + def self.read_file(filename, encoding, force_transcode = false) + content = File.open filename, "rb" do |f| f.read end + content.gsub!("\r\n", "\n") if RUBY_PLATFORM =~ /mswin|mingw/ + + utf8 = content.sub!(/\A\xef\xbb\xbf/, '') + + enc = Encoding.detect_encoding content + content = Encoding.change_encoding content, enc if enc + + begin + encoding ||= ::Encoding.default_external + orig_encoding = content.encoding + + if not orig_encoding.ascii_compatible? + content = content.encode encoding + elsif utf8 + content = Encoding.change_encoding content, ::Encoding::UTF_8 + content = content.encode encoding + else + # assume the content is in our output encoding + content = Encoding.change_encoding content, encoding + end + + unless content.valid_encoding? + # revert and try to transcode + content = Encoding.change_encoding content, orig_encoding + content = content.encode encoding + end + + unless content.valid_encoding? + warn "unable to convert #{filename} to #{encoding}, skipping" + content = nil + end + rescue ::Encoding::InvalidByteSequenceError, + ::Encoding::UndefinedConversionError => e + if force_transcode + content = Encoding.change_encoding content, orig_encoding + content = content.encode(encoding, + :invalid => :replace, + :undef => :replace, + :replace => '?') + return content + else + warn "unable to convert #{e.message} for #{filename}, skipping" + return nil + end end - unless content.valid_encoding? - warn "unable to convert #{filename} to #{encoding}, skipping" - content = nil - end - rescue Encoding::InvalidByteSequenceError, - Encoding::UndefinedConversionError => e - if force_transcode - content = RDoc::Encoding.change_encoding content, orig_encoding - content = content.encode(encoding, - :invalid => :replace, - :undef => :replace, - :replace => '?') - return content - else - warn "unable to convert #{e.message} for #{filename}, skipping" - return nil - end + content + rescue ArgumentError => e + raise unless e.message =~ /unknown encoding name - (.*)/ + warn "unknown encoding name \"#{$1}\" for #{filename}, skipping" + nil + rescue Errno::EISDIR, Errno::ENOENT + nil end - content - rescue ArgumentError => e - raise unless e.message =~ /unknown encoding name - (.*)/ - warn "unknown encoding name \"#{$1}\" for #{filename}, skipping" - nil - rescue Errno::EISDIR, Errno::ENOENT - nil - end + ## + # Detects the encoding of +string+ based on the magic comment - ## - # Detects the encoding of +string+ based on the magic comment + def self.detect_encoding(string) + result = HEADER_REGEXP.match string + name = result && result[:name] - def self.detect_encoding(string) - result = HEADER_REGEXP.match string - name = result && result[:name] - - name ? Encoding.find(name) : nil - end + name ? ::Encoding.find(name) : nil + end - ## - # Removes magic comments and shebang + ## + # Removes magic comments and shebang - def self.remove_magic_comment(string) - string.sub HEADER_REGEXP do |s| - s.gsub(/[^\n]/, '') + def self.remove_magic_comment(string) + string.sub HEADER_REGEXP do |s| + s.gsub(/[^\n]/, '') + end end - end - ## - # Changes encoding based on +encoding+ without converting and returns new - # string - - def self.change_encoding(text, encoding) - if text.kind_of? RDoc::Comment - text.encode! encoding - else - String.new text, encoding: encoding + ## + # Changes encoding based on +encoding+ without converting and returns new + # string + + def self.change_encoding(text, encoding) + if text.kind_of? Comment + text.encode! encoding + else + String.new text, encoding: encoding + end end - end + end end diff --git a/lib/rdoc/erb_partial.rb b/lib/rdoc/erb_partial.rb index bad02ea706..22695ba759 100644 --- a/lib/rdoc/erb_partial.rb +++ b/lib/rdoc/erb_partial.rb @@ -1,18 +1,20 @@ # frozen_string_literal: true -## -# Allows an ERB template to be rendered in the context (binding) of an -# existing ERB template evaluation. +module RDoc + ## + # Allows an ERB template to be rendered in the context (binding) of an + # existing ERB template evaluation. -class RDoc::ERBPartial < ERB + class ERBPartial < ERB - ## - # Overrides +compiler+ startup to set the +eoutvar+ to an empty string only - # if it isn't already set. + ## + # Overrides +compiler+ startup to set the +eoutvar+ to an empty string only + # if it isn't already set. - def set_eoutvar(compiler, eoutvar = '_erbout') - super + def set_eoutvar(compiler, eoutvar = '_erbout') + super - compiler.pre_cmd = ["#{eoutvar} ||= +''"] - end + compiler.pre_cmd = ["#{eoutvar} ||= +''"] + end + end end diff --git a/lib/rdoc/erbio.rb b/lib/rdoc/erbio.rb index e955eed811..a9a558a65e 100644 --- a/lib/rdoc/erbio.rb +++ b/lib/rdoc/erbio.rb @@ -1,37 +1,39 @@ # frozen_string_literal: true require 'erb' -## -# A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson -# and Masatoshi SEKI. -# -# To use: -# -# erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil -# -# File.open 'hello.txt', 'w' do |io| -# erbio.result binding -# end -# -# Note that binding must enclose the io you wish to output on. +module RDoc + ## + # A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson + # and Masatoshi SEKI. + # + # To use: + # + # erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil + # + # File.open 'hello.txt', 'w' do |io| + # erbio.result binding + # end + # + # Note that binding must enclose the io you wish to output on. -class RDoc::ERBIO < ERB + class ERBIO < ERB - ## - # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize + ## + # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize - def initialize(str, trim_mode: nil, eoutvar: 'io') - super(str, trim_mode: trim_mode, eoutvar: eoutvar) - end + def initialize(str, trim_mode: nil, eoutvar: 'io') + super(str, trim_mode: trim_mode, eoutvar: eoutvar) + end - ## - # Instructs +compiler+ how to write to +io_variable+ + ## + # Instructs +compiler+ how to write to +io_variable+ - def set_eoutvar(compiler, io_variable) - compiler.put_cmd = "#{io_variable}.write" - compiler.insert_cmd = "#{io_variable}.write" - compiler.pre_cmd = [] - compiler.post_cmd = [] - end + def set_eoutvar(compiler, io_variable) + compiler.put_cmd = "#{io_variable}.write" + compiler.insert_cmd = "#{io_variable}.write" + compiler.pre_cmd = [] + compiler.post_cmd = [] + end + end end diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb index e8a14d4a66..edc5f34dc5 100644 --- a/lib/rdoc/generator.rb +++ b/lib/rdoc/generator.rb @@ -1,52 +1,54 @@ # frozen_string_literal: true -## -# RDoc uses generators to turn parsed source code in the form of an -# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML -# generator RDoc::Generator::Darkfish and an ri data generator -# RDoc::Generator::RI. -# -# == Registering a Generator -# -# Generators are registered by calling RDoc::RDoc.add_generator with the class -# of the generator: -# -# class My::Awesome::Generator -# RDoc::RDoc.add_generator self -# end -# -# == Adding Options to +rdoc+ -# -# Before option processing in +rdoc+, RDoc::Options will call ::setup_options -# on the generator class with an RDoc::Options instance. The generator can -# use RDoc::Options#option_parser to add command-line options to the +rdoc+ -# tool. See RDoc::Options@Custom+Options for an example and see OptionParser -# for details on how to add options. -# -# You can extend the RDoc::Options instance with additional accessors for your -# generator. -# -# == Generator Instantiation -# -# After parsing, RDoc::RDoc will instantiate a generator by calling -# #initialize with an RDoc::Store instance and an RDoc::Options instance. -# -# The RDoc::Store instance holds documentation for parsed source code. In -# RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading -# a generator from RDoc 3 and earlier you should only need to replace -# RDoc::TopLevel with the store instance. -# -# RDoc will then call #generate on the generator instance. You can use the -# various methods on RDoc::Store and in the RDoc::CodeObject tree to create -# your desired output format. +module RDoc + ## + # RDoc uses generators to turn parsed source code in the form of an + # RDoc::CodeObject tree into some form of output. RDoc comes with the HTML + # generator RDoc::Generator::Darkfish and an ri data generator + # RDoc::Generator::RI. + # + # == Registering a Generator + # + # Generators are registered by calling RDoc::RDoc.add_generator with the class + # of the generator: + # + # class My::Awesome::Generator + # RDoc::RDoc.add_generator self + # end + # + # == Adding Options to +rdoc+ + # + # Before option processing in +rdoc+, RDoc::Options will call ::setup_options + # on the generator class with an RDoc::Options instance. The generator can + # use RDoc::Options#option_parser to add command-line options to the +rdoc+ + # tool. See RDoc::Options@Custom+Options for an example and see OptionParser + # for details on how to add options. + # + # You can extend the RDoc::Options instance with additional accessors for your + # generator. + # + # == Generator Instantiation + # + # After parsing, RDoc::RDoc will instantiate a generator by calling + # #initialize with an RDoc::Store instance and an RDoc::Options instance. + # + # The RDoc::Store instance holds documentation for parsed source code. In + # RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading + # a generator from RDoc 3 and earlier you should only need to replace + # RDoc::TopLevel with the store instance. + # + # RDoc will then call #generate on the generator instance. You can use the + # various methods on RDoc::Store and in the RDoc::CodeObject tree to create + # your desired output format. -module RDoc::Generator + module Generator - autoload :Markup, "#{__dir__}/generator/markup" + autoload :Markup, "#{__dir__}/generator/markup" - autoload :Aliki, "#{__dir__}/generator/aliki" - autoload :Darkfish, "#{__dir__}/generator/darkfish" - autoload :JsonIndex, "#{__dir__}/generator/json_index" - autoload :RI, "#{__dir__}/generator/ri" - autoload :POT, "#{__dir__}/generator/pot" + autoload :Aliki, "#{__dir__}/generator/aliki" + autoload :Darkfish, "#{__dir__}/generator/darkfish" + autoload :JsonIndex, "#{__dir__}/generator/json_index" + autoload :RI, "#{__dir__}/generator/ri" + autoload :POT, "#{__dir__}/generator/pot" + end end diff --git a/lib/rdoc/generator/aliki.rb b/lib/rdoc/generator/aliki.rb index bb8628c56a..5157283b3a 100644 --- a/lib/rdoc/generator/aliki.rb +++ b/lib/rdoc/generator/aliki.rb @@ -2,203 +2,207 @@ require 'uri' -## -# Aliki theme for RDoc documentation -# -# Author: Stan Lo -# +module RDoc + module Generator + ## + # Aliki theme for RDoc documentation + # + # Author: Stan Lo + # + + class Aliki < Generator::Darkfish + DESCRIPTION = 'HTML generator, written by Stan Lo' + + RDoc.add_generator self + + def initialize(store, options) + super + aliki_template_dir = File.expand_path(File.join(__dir__, 'template', 'aliki')) + @template_dir = Pathname.new(aliki_template_dir) + end -class RDoc::Generator::Aliki < RDoc::Generator::Darkfish - DESCRIPTION = 'HTML generator, written by Stan Lo' + ## + # Generate documentation. Overrides Darkfish to use Aliki's own search index + # instead of the JsonIndex generator. - RDoc::RDoc.add_generator self + def generate + setup - def initialize(store, options) - super - aliki_template_dir = File.expand_path(File.join(__dir__, 'template', 'aliki')) - @template_dir = Pathname.new(aliki_template_dir) - end + write_style_sheet + generate_index + generate_class_files + generate_file_files + generate_table_of_contents + write_search_index - ## - # Generate documentation. Overrides Darkfish to use Aliki's own search index - # instead of the JsonIndex generator. + copy_static - def generate - setup + rescue => e + debug_msg "%s: %s\n %s" % [ + e.class.name, e.message, e.backtrace.join("\n ") + ] - write_style_sheet - generate_index - generate_class_files - generate_file_files - generate_table_of_contents - write_search_index + raise + end - copy_static + ## + # Copy only the static assets required by the Aliki theme. Unlike Darkfish we + # don't ship embedded fonts or image sprites, so limit the asset list to keep + # generated documentation lightweight. - rescue => e - debug_msg "%s: %s\n %s" % [ - e.class.name, e.message, e.backtrace.join("\n ") - ] + def write_style_sheet + debug_msg "Copying Aliki static files" + options = { verbose: $DEBUG_RDOC, noop: @dry_run } - raise - end + install_rdoc_static_file @template_dir + 'css/rdoc.css', "./css/rdoc.css", options - ## - # Copy only the static assets required by the Aliki theme. Unlike Darkfish we - # don't ship embedded fonts or image sprites, so limit the asset list to keep - # generated documentation lightweight. + unless @options.template_stylesheets.empty? + FileUtils.cp @options.template_stylesheets, '.', **options + end - def write_style_sheet - debug_msg "Copying Aliki static files" - options = { verbose: $DEBUG_RDOC, noop: @dry_run } + Dir[(@template_dir + 'js/**/*').to_s].each do |path| + next if File.directory?(path) + next if File.basename(path).start_with?('.') - install_rdoc_static_file @template_dir + 'css/rdoc.css', "./css/rdoc.css", options + dst = Pathname.new(path).relative_path_from(@template_dir) - unless @options.template_stylesheets.empty? - FileUtils.cp @options.template_stylesheets, '.', **options - end + install_rdoc_static_file @template_dir + path, dst, options + end + end - Dir[(@template_dir + 'js/**/*').to_s].each do |path| - next if File.directory?(path) - next if File.basename(path).start_with?('.') + ## + # Build a search index array for Aliki's searcher. - dst = Pathname.new(path).relative_path_from(@template_dir) + def build_search_index + setup - install_rdoc_static_file @template_dir + path, dst, options - end - end + index = [] - ## - # Build a search index array for Aliki's searcher. + @classes.each do |klass| + next unless klass.display? - def build_search_index - setup + index << build_class_module_entry(klass) - index = [] + klass.constants.each do |const| + next unless const.display? - @classes.each do |klass| - next unless klass.display? + index << build_constant_entry(const, klass) + end + end - index << build_class_module_entry(klass) + @methods.each do |method| + next unless method.display? - klass.constants.each do |const| - next unless const.display? + index << build_method_entry(method) + end - index << build_constant_entry(const, klass) + index end - end - - @methods.each do |method| - next unless method.display? - - index << build_method_entry(method) - end - - index - end - - ## - # Write the search index as a JavaScript file - # Format: var search_data = { index: [...] } - # - # We still write to a .js instead of a .json because loading a JSON file triggers CORS check in browsers. - # And if we simply inspect the generated pages using file://, which is often the case due to lack of the server mode, - # the JSON file will be blocked by the browser. - def write_search_index - debug_msg "Writing Aliki search index" + ## + # Write the search index as a JavaScript file + # Format: var search_data = { index: [...] } + # + # We still write to a .js instead of a .json because loading a JSON file triggers CORS check in browsers. + # And if we simply inspect the generated pages using file://, which is often the case due to lack of the server mode, + # the JSON file will be blocked by the browser. - index = build_search_index + def write_search_index + debug_msg "Writing Aliki search index" - FileUtils.mkdir_p 'js' unless @dry_run + index = build_search_index - search_index_path = 'js/search_data.js' - return if @dry_run + FileUtils.mkdir_p 'js' unless @dry_run - data = { index: index } - File.write search_index_path, "var search_data = #{JSON.generate(data)};" - end + search_index_path = 'js/search_data.js' + return if @dry_run - ## - # Returns the type signature of +method_attr+ as HTML with linked type names. - # Returns nil if no type signature is present. + data = { index: index } + File.write search_index_path, "var search_data = #{JSON.generate(data)};" + end - def type_signature_html(method_attr, from_path) - lines = method_attr.type_signature_lines || @store.rbs_signature_for(method_attr) - return unless lines + ## + # Returns the type signature of +method_attr+ as HTML with linked type names. + # Returns nil if no type signature is present. - RDoc::RbsHelper.signature_to_html( - lines, - lookup: @store.type_name_lookup, - from_path: from_path - ) - end + def type_signature_html(method_attr, from_path) + lines = method_attr.type_signature_lines || @store.rbs_signature_for(method_attr) + return unless lines - ## - # Resolves a URL for use in templates. Absolute URLs are returned unchanged. - # Relative URLs are prefixed with rel_prefix to ensure they resolve correctly from any page. + RbsHelper.signature_to_html( + lines, + lookup: @store.type_name_lookup, + from_path: from_path + ) + end - def resolve_url(rel_prefix, url) - uri = URI.parse(url) - if uri.absolute? - url - else - "#{rel_prefix}/#{url}" - end - rescue URI::InvalidURIError - "#{rel_prefix}/#{url}" - end + ## + # Resolves a URL for use in templates. Absolute URLs are returned unchanged. + # Relative URLs are prefixed with rel_prefix to ensure they resolve correctly from any page. + + def resolve_url(rel_prefix, url) + uri = URI.parse(url) + if uri.absolute? + url + else + "#{rel_prefix}/#{url}" + end + rescue URI::InvalidURIError + "#{rel_prefix}/#{url}" + end - private + private - def template_encoding - Encoding::UTF_8 - end + def template_encoding + ::Encoding::UTF_8 + end - def build_class_module_entry(klass) - type = case klass - when RDoc::NormalClass then 'class' - when RDoc::NormalModule then 'module' - else 'class' - end - - entry = { - name: klass.name, - full_name: klass.full_name, - type: type, - path: klass.path - } - - snippet = klass.search_snippet - entry[:snippet] = snippet unless snippet.empty? - entry - end + def build_class_module_entry(klass) + type = case klass + when NormalClass then 'class' + when NormalModule then 'module' + else 'class' + end + + entry = { + name: klass.name, + full_name: klass.full_name, + type: type, + path: klass.path + } + + snippet = klass.search_snippet + entry[:snippet] = snippet unless snippet.empty? + entry + end - def build_method_entry(method) - type = method.singleton ? 'class_method' : 'instance_method' + def build_method_entry(method) + type = method.singleton ? 'class_method' : 'instance_method' - entry = { - name: method.name, - full_name: method.full_name, - type: type, - path: method.path - } + entry = { + name: method.name, + full_name: method.full_name, + type: type, + path: method.path + } - snippet = method.search_snippet - entry[:snippet] = snippet unless snippet.empty? - entry - end + snippet = method.search_snippet + entry[:snippet] = snippet unless snippet.empty? + entry + end - def build_constant_entry(const, parent) - entry = { - name: const.name, - full_name: "#{parent.full_name}::#{const.name}", - type: 'constant', - path: parent.path - } - - snippet = const.search_snippet - entry[:snippet] = snippet unless snippet.empty? - entry + def build_constant_entry(const, parent) + entry = { + name: const.name, + full_name: "#{parent.full_name}::#{const.name}", + type: 'constant', + path: parent.path + } + + snippet = const.search_snippet + entry[:snippet] = snippet unless snippet.empty? + entry + end + end end end diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb index b728a3f973..1919b9aa0f 100644 --- a/lib/rdoc/generator/darkfish.rb +++ b/lib/rdoc/generator/darkfish.rb @@ -6,605 +6,607 @@ require 'pathname' require_relative 'markup' -## -# Darkfish RDoc HTML Generator -# -# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $ -# -# == Author/s -# * Michael Granger (ged@FaerieMUD.org) -# -# == Contributors -# * Mahlon E. Smith (mahlon@martini.nu) -# * Eric Hodel (drbrain@segment7.net) -# -# == License -# -# Copyright (c) 2007, 2008, Michael Granger. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of the author/s, nor the names of the project's -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# == Attributions -# -# Darkfish uses the {Silk Icons}[http://www.famfamfam.com/lab/icons/silk/] set -# by Mark James. - -class RDoc::Generator::Darkfish - - RDoc::RDoc.add_generator self - - include ERB::Util - - ## - # Stylesheets, fonts, etc. that are included in RDoc. - - BUILTIN_STYLE_ITEMS = # :nodoc: - %w[ - css/fonts.css - fonts/Lato-Light.ttf - fonts/Lato-LightItalic.ttf - fonts/Lato-Regular.ttf - fonts/Lato-RegularItalic.ttf - fonts/SourceCodePro-Bold.ttf - fonts/SourceCodePro-Regular.ttf - css/rdoc.css - ] - - ## - # Description of this generator - - DESCRIPTION = 'HTML generator, written by Michael Granger' - - ## - # The relative path to style sheets and javascript. By default this is set - # the same as the rel_prefix. - - attr_accessor :asset_rel_path - - ## - # The path to generate files into, combined with --op from the - # options for a full path. - - attr_reader :base_dir - - ## - # Classes and modules to be used by this generator, not necessarily - # displayed. See also #modsort - - attr_reader :classes - - ## - # No files will be written when dry_run is true. - - attr_accessor :dry_run - - ## - # When false the generate methods return a String instead of writing to a - # file. The default is true. - - attr_accessor :file_output - - ## - # Files to be displayed by this generator - - attr_reader :files - - ## - # The JSON index generator for this Darkfish generator - - attr_reader :json_index +module RDoc + module Generator + ## + # Darkfish RDoc HTML Generator + # + # $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $ + # + # == Author/s + # * Michael Granger (ged@FaerieMUD.org) + # + # == Contributors + # * Mahlon E. Smith (mahlon@martini.nu) + # * Eric Hodel (drbrain@segment7.net) + # + # == License + # + # Copyright (c) 2007, 2008, Michael Granger. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # * Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # * Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # * Neither the name of the author/s, nor the names of the project's + # contributors may be used to endorse or promote products derived from this + # software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # == Attributions + # + # Darkfish uses the {Silk Icons}[http://www.famfamfam.com/lab/icons/silk/] set + # by Mark James. + + class Darkfish + + RDoc.add_generator self + + include ERB::Util + + ## + # Stylesheets, fonts, etc. that are included in RDoc. + + BUILTIN_STYLE_ITEMS = # :nodoc: + %w[ + css/fonts.css + fonts/Lato-Light.ttf + fonts/Lato-LightItalic.ttf + fonts/Lato-Regular.ttf + fonts/Lato-RegularItalic.ttf + fonts/SourceCodePro-Bold.ttf + fonts/SourceCodePro-Regular.ttf + css/rdoc.css + ] + + ## + # Description of this generator + + DESCRIPTION = 'HTML generator, written by Michael Granger' + + ## + # The relative path to style sheets and javascript. By default this is set + # the same as the rel_prefix. + + attr_accessor :asset_rel_path + + ## + # The path to generate files into, combined with --op from the + # options for a full path. + + attr_reader :base_dir + + ## + # Classes and modules to be used by this generator, not necessarily + # displayed. See also #modsort + + attr_reader :classes + + ## + # No files will be written when dry_run is true. + + attr_accessor :dry_run + + ## + # When false the generate methods return a String instead of writing to a + # file. The default is true. + + attr_accessor :file_output + + ## + # Files to be displayed by this generator + + attr_reader :files + + ## + # The JSON index generator for this Darkfish generator + + attr_reader :json_index - ## - # Methods to be displayed by this generator + ## + # Methods to be displayed by this generator - attr_reader :methods + attr_reader :methods - ## - # Sorted list of classes and modules to be displayed by this generator + ## + # Sorted list of classes and modules to be displayed by this generator - attr_reader :modsort + attr_reader :modsort - ## - # The RDoc::Store that is the source of the generated content + ## + # The RDoc::Store that is the source of the generated content - attr_reader :store + attr_reader :store - ## - # The directory where the template files live + ## + # The directory where the template files live - attr_reader :template_dir # :nodoc: + attr_reader :template_dir # :nodoc: - ## - # The output directory + ## + # The output directory - attr_reader :outputdir + attr_reader :outputdir - ## - # Initialize a few instance variables before we start + ## + # Initialize a few instance variables before we start - def initialize(store, options) - @store = store - @options = options + def initialize(store, options) + @store = store + @options = options - @asset_rel_path = '' - @base_dir = Pathname.pwd.expand_path - @dry_run = @options.dry_run - @file_output = true - @template_dir = Pathname.new options.template_dir - @template_cache = {} + @asset_rel_path = '' + @base_dir = Pathname.pwd.expand_path + @dry_run = @options.dry_run + @file_output = true + @template_dir = Pathname.new options.template_dir + @template_cache = {} - @classes = nil - @context = nil - @files = nil - @methods = nil - @modsort = nil + @classes = nil + @context = nil + @files = nil + @methods = nil + @modsort = nil - @json_index = RDoc::Generator::JsonIndex.new self, options - end + @json_index = Generator::JsonIndex.new self, options + end - ## - # Output progress information if debugging is enabled + ## + # Output progress information if debugging is enabled - def debug_msg(*msg) - return unless $DEBUG_RDOC - $stderr.puts(*msg) - end + def debug_msg(*msg) + return unless $DEBUG_RDOC + $stderr.puts(*msg) + end - ## - # Create the directories the generated docs will live in if they don't - # already exist. + ## + # Create the directories the generated docs will live in if they don't + # already exist. - def gen_sub_directories - @outputdir.mkpath - end + def gen_sub_directories + @outputdir.mkpath + end - ## - # Copy over the stylesheet into the appropriate place in the output - # directory. + ## + # Copy over the stylesheet into the appropriate place in the output + # directory. - def write_style_sheet - debug_msg "Copying static files" - options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } + def write_style_sheet + debug_msg "Copying static files" + options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } - BUILTIN_STYLE_ITEMS.each do |item| - install_rdoc_static_file @template_dir + item, "./#{item}", options - end + BUILTIN_STYLE_ITEMS.each do |item| + install_rdoc_static_file @template_dir + item, "./#{item}", options + end - unless @options.template_stylesheets.empty? - FileUtils.cp @options.template_stylesheets, '.', **options - end + unless @options.template_stylesheets.empty? + FileUtils.cp @options.template_stylesheets, '.', **options + end - Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path| - next if File.directory? path - next if File.basename(path) =~ /^\./ + Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path| + next if File.directory? path + next if File.basename(path) =~ /^\./ - dst = Pathname.new(path).relative_path_from @template_dir + dst = Pathname.new(path).relative_path_from @template_dir - install_rdoc_static_file @template_dir + path, dst, options - end - end + install_rdoc_static_file @template_dir + path, dst, options + end + end - ## - # Build the initial indices and output objects based on an array of TopLevel - # objects containing the extracted information. + ## + # Build the initial indices and output objects based on an array of TopLevel + # objects containing the extracted information. - def generate - setup + def generate + setup - write_style_sheet - generate_index - generate_class_files - generate_file_files - generate_table_of_contents - @json_index.generate - @json_index.generate_gzipped + write_style_sheet + generate_index + generate_class_files + generate_file_files + generate_table_of_contents + @json_index.generate + @json_index.generate_gzipped - copy_static + copy_static - rescue => e - debug_msg "%s: %s\n %s" % [ - e.class.name, e.message, e.backtrace.join("\n ") - ] + rescue => e + debug_msg "%s: %s\n %s" % [ + e.class.name, e.message, e.backtrace.join("\n ") + ] - raise - end + raise + end - ## - # Copies static files from the static_path into the output directory + ## + # Copies static files from the static_path into the output directory - def copy_static - return if @options.static_path.empty? + def copy_static + return if @options.static_path.empty? - fu_options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } + fu_options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } - @options.static_path.each do |path| - unless File.directory? path - FileUtils.install path, @outputdir, **fu_options.merge(:mode => 0644) - next - end + @options.static_path.each do |path| + unless File.directory? path + FileUtils.install path, @outputdir, **fu_options.merge(:mode => 0644) + next + end - Dir.chdir path do - Dir[File.join('**', '*')].each do |entry| - dest_file = @outputdir + entry + Dir.chdir path do + Dir[File.join('**', '*')].each do |entry| + dest_file = @outputdir + entry - if File.directory? entry - FileUtils.mkdir_p entry, **fu_options - else - FileUtils.install entry, dest_file, **fu_options.merge(:mode => 0644) + if File.directory? entry + FileUtils.mkdir_p entry, **fu_options + else + FileUtils.install entry, dest_file, **fu_options.merge(:mode => 0644) + end + end end end end - end - end - ## - # Return a list of the documented modules sorted by salience first, then - # by name. + ## + # Return a list of the documented modules sorted by salience first, then + # by name. - def get_sorted_module_list(classes) - classes.select do |klass| - klass.display? - end.sort - end + def get_sorted_module_list(classes) + classes.select do |klass| + klass.display? + end.sort + end - ## - # Generate an index page which lists all the classes which are documented. + ## + # Generate an index page which lists all the classes which are documented. - def generate_index - template_file = @template_dir + 'index.rhtml' - return unless template_file.exist? + def generate_index + template_file = @template_dir + 'index.rhtml' + return unless template_file.exist? - debug_msg "Rendering the index page..." + debug_msg "Rendering the index page..." - out_file = @base_dir + @options.op_dir + 'index.html' - rel_prefix = @outputdir.relative_path_from out_file.dirname - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + out_file = @base_dir + @options.op_dir + 'index.html' + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - asset_rel_prefix = rel_prefix + @asset_rel_path + asset_rel_prefix = rel_prefix + @asset_rel_path - @title = @options.title - @main_page = @files.find { |f| f.full_name == @options.main_page } + @title = @options.title + @main_page = @files.find { |f| f.full_name == @options.main_page } - render_template template_file, out_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - # some partials rely on the presence of current variable to render - here.local_variable_set(:current, @main_page) if @main_page - here - end - rescue => e - error = RDoc::Error.new \ - "error generating index.html: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace + render_template template_file, out_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + # some partials rely on the presence of current variable to render + here.local_variable_set(:current, @main_page) if @main_page + here + end + rescue => e + error = Error.new \ + "error generating index.html: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - raise error - end + raise error + end - ## - # Generates a class file for +klass+ + ## + # Generates a class file for +klass+ - def generate_class(klass, template_file = nil) - # This is used to auto-collapse Pages section on class/module pages - @inside_class_file = true - current = klass + def generate_class(klass, template_file = nil) + # This is used to auto-collapse Pages section on class/module pages + @inside_class_file = true + current = klass - template_file ||= @template_dir + 'class.rhtml' + template_file ||= @template_dir + 'class.rhtml' - debug_msg " working on %s (%s)" % [klass.full_name, klass.path] - out_file = @outputdir + klass.path - rel_prefix = @outputdir.relative_path_from out_file.dirname - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + debug_msg " working on %s (%s)" % [klass.full_name, klass.path] + out_file = @outputdir + klass.path + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - asset_rel_prefix = rel_prefix + @asset_rel_path + asset_rel_prefix = rel_prefix + @asset_rel_path - breadcrumb = # used in templates - breadcrumb = generate_nesting_namespaces_breadcrumb(current, rel_prefix) + breadcrumb = # used in templates + breadcrumb = generate_nesting_namespaces_breadcrumb(current, rel_prefix) - @title = "#{klass.type} #{klass.full_name} - #{@options.title}" + @title = "#{klass.type} #{klass.full_name} - #{@options.title}" - debug_msg " rendering #{out_file}" - render_template template_file, out_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - here - end - ensure - @inside_class_file = false - end + debug_msg " rendering #{out_file}" + render_template template_file, out_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + here + end + ensure + @inside_class_file = false + end - ## - # Generate a documentation file for each class and module + ## + # Generate a documentation file for each class and module - def generate_class_files - template_file = @template_dir + 'class.rhtml' - template_file = @template_dir + 'classpage.rhtml' unless - template_file.exist? - return unless template_file.exist? - debug_msg "Generating class documentation in #{@outputdir}" + def generate_class_files + template_file = @template_dir + 'class.rhtml' + template_file = @template_dir + 'classpage.rhtml' unless + template_file.exist? + return unless template_file.exist? + debug_msg "Generating class documentation in #{@outputdir}" - current = nil + current = nil - # Document files are generated only for non-alias classes/modules - @classes.reject(&:is_alias_for).each do |klass| + # Document files are generated only for non-alias classes/modules + @classes.reject(&:is_alias_for).each do |klass| - current = klass + current = klass - generate_class klass, template_file - end - rescue => e - error = RDoc::Error.new \ - "error generating #{current.path}: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace + generate_class klass, template_file + end + rescue => e + error = Error.new \ + "error generating #{current.path}: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - raise error - end + raise error + end - ## - # Generate a documentation file for each file + ## + # Generate a documentation file for each file - def generate_file_files - page_file = @template_dir + 'page.rhtml' - fileinfo_file = @template_dir + 'fileinfo.rhtml' + def generate_file_files + page_file = @template_dir + 'page.rhtml' + fileinfo_file = @template_dir + 'fileinfo.rhtml' - # for legacy templates - filepage_file = @template_dir + 'filepage.rhtml' unless - page_file.exist? or fileinfo_file.exist? + # for legacy templates + filepage_file = @template_dir + 'filepage.rhtml' unless + page_file.exist? or fileinfo_file.exist? - return unless - page_file.exist? or fileinfo_file.exist? or filepage_file.exist? + return unless + page_file.exist? or fileinfo_file.exist? or filepage_file.exist? - debug_msg "Generating file documentation in #{@outputdir}" + debug_msg "Generating file documentation in #{@outputdir}" - out_file = nil - current = nil + out_file = nil + current = nil - @files.each do |file| - current = file + @files.each do |file| + current = file - next if file.text? && file.full_name == @options.main_page + next if file.text? && file.full_name == @options.main_page - if file.text? and page_file.exist? - generate_page file - next - end + if file.text? and page_file.exist? + generate_page file + next + end - template_file = nil - out_file = @outputdir + file.path - debug_msg " working on %s (%s)" % [file.full_name, out_file] - rel_prefix = @outputdir.relative_path_from out_file.dirname - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + template_file = nil + out_file = @outputdir + file.path + debug_msg " working on %s (%s)" % [file.full_name, out_file] + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + asset_rel_prefix = rel_prefix + @asset_rel_path + + unless filepage_file + if file.text? + next unless page_file.exist? + template_file = page_file + @title = file.page_name + else + next unless fileinfo_file.exist? + template_file = fileinfo_file + @title = "File: #{file.base_name}" + end + end - asset_rel_prefix = rel_prefix + @asset_rel_path + @title += " - #{@options.title}" + template_file ||= filepage_file - unless filepage_file - if file.text? - next unless page_file.exist? - template_file = page_file - @title = file.page_name - else - next unless fileinfo_file.exist? - template_file = fileinfo_file - @title = "File: #{file.base_name}" + render_template template_file, out_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + here.local_variable_set(:current, current) + here + end end - end + rescue => e + error = + Error.new "error generating #{out_file}: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - @title += " - #{@options.title}" - template_file ||= filepage_file - - render_template template_file, out_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - here.local_variable_set(:current, current) - here + raise error end - end - rescue => e - error = - RDoc::Error.new "error generating #{out_file}: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace - raise error - end - - ## - # Generate a page file for +file+ + ## + # Generate a page file for +file+ - def generate_page(file) - template_file = @template_dir + 'page.rhtml' + def generate_page(file) + template_file = @template_dir + 'page.rhtml' - out_file = @outputdir + file.path - debug_msg " working on %s (%s)" % [file.full_name, out_file] - rel_prefix = @outputdir.relative_path_from out_file.dirname - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + out_file = @outputdir + file.path + debug_msg " working on %s (%s)" % [file.full_name, out_file] + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - current = file - asset_rel_prefix = rel_prefix + @asset_rel_path + current = file + asset_rel_prefix = rel_prefix + @asset_rel_path - @title = "#{file.page_name} - #{@options.title}" + @title = "#{file.page_name} - #{@options.title}" - debug_msg " rendering #{out_file}" - render_template template_file, out_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:current, current) - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - here - end - end + debug_msg " rendering #{out_file}" + render_template template_file, out_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:current, current) + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + here + end + end - ## - # Generates the 404 page for the RDoc servlet + ## + # Generates the 404 page for the RDoc servlet - def generate_servlet_not_found(message) - template_file = @template_dir + 'servlet_not_found.rhtml' - return unless template_file.exist? + def generate_servlet_not_found(message) + template_file = @template_dir + 'servlet_not_found.rhtml' + return unless template_file.exist? - debug_msg "Rendering the servlet 404 Not Found page..." + debug_msg "Rendering the servlet 404 Not Found page..." - rel_prefix = rel_prefix = '' - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + rel_prefix = rel_prefix = '' + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - asset_rel_prefix = '' + asset_rel_prefix = '' - @title = 'Not Found' + @title = 'Not Found' - render_template template_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - here - end - rescue => e - error = RDoc::Error.new \ - "error generating servlet_not_found: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace + render_template template_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + here + end + rescue => e + error = Error.new \ + "error generating servlet_not_found: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - raise error - end + raise error + end - ## - # Generates the servlet root page for the RDoc servlet + ## + # Generates the servlet root page for the RDoc servlet - def generate_servlet_root(installed) - template_file = @template_dir + 'servlet_root.rhtml' - return unless template_file.exist? + def generate_servlet_root(installed) + template_file = @template_dir + 'servlet_root.rhtml' + return unless template_file.exist? - debug_msg 'Rendering the servlet root page...' + debug_msg 'Rendering the servlet root page...' - rel_prefix = '.' - asset_rel_prefix = rel_prefix - search_index_rel_prefix = asset_rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + rel_prefix = '.' + asset_rel_prefix = rel_prefix + search_index_rel_prefix = asset_rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - @title = 'Local RDoc Documentation' + @title = 'Local RDoc Documentation' - render_template template_file do |io| binding end - rescue => e - error = RDoc::Error.new \ - "error generating servlet_root: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace + render_template template_file do |io| binding end + rescue => e + error = Error.new \ + "error generating servlet_root: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - raise error - end + raise error + end - ## - # Generate an index page which lists all the classes which are documented. + ## + # Generate an index page which lists all the classes which are documented. - def generate_table_of_contents - template_file = @template_dir + 'table_of_contents.rhtml' - return unless template_file.exist? + def generate_table_of_contents + template_file = @template_dir + 'table_of_contents.rhtml' + return unless template_file.exist? - debug_msg "Rendering the Table of Contents..." + debug_msg "Rendering the Table of Contents..." - out_file = @outputdir + 'table_of_contents.html' - rel_prefix = @outputdir.relative_path_from out_file.dirname - search_index_rel_prefix = rel_prefix - search_index_rel_prefix += @asset_rel_path if @file_output + out_file = @outputdir + 'table_of_contents.html' + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output - asset_rel_prefix = rel_prefix + @asset_rel_path + asset_rel_prefix = rel_prefix + @asset_rel_path - @title = "Table of Contents - #{@options.title}" + @title = "Table of Contents - #{@options.title}" - render_template template_file, out_file do |io| - here = binding - # suppress 1.9.3 warning - here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) - here - end - rescue => e - error = RDoc::Error.new \ - "error generating table_of_contents.html: #{e.message} (#{e.class})" - error.set_backtrace e.backtrace + render_template template_file, out_file do |io| + here = binding + # suppress 1.9.3 warning + here.local_variable_set(:asset_rel_prefix, asset_rel_prefix) + here + end + rescue => e + error = Error.new \ + "error generating table_of_contents.html: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace - raise error - end + raise error + end - def install_rdoc_static_file(source, destination, options) # :nodoc: - return unless source.exist? + def install_rdoc_static_file(source, destination, options) # :nodoc: + return unless source.exist? - begin - FileUtils.mkdir_p File.dirname(destination), **options + begin + FileUtils.mkdir_p File.dirname(destination), **options - begin - FileUtils.ln source, destination, **options - rescue Errno::EEXIST - FileUtils.rm destination - retry + begin + FileUtils.ln source, destination, **options + rescue Errno::EEXIST + FileUtils.rm destination + retry + end + rescue + FileUtils.cp source, destination, **options + end end - rescue - FileUtils.cp source, destination, **options - end - end - ## - # Prepares for generation of output from the current directory + ## + # Prepares for generation of output from the current directory - def setup - return if instance_variable_defined? :@outputdir + def setup + return if instance_variable_defined? :@outputdir - @outputdir = Pathname.new(@options.op_dir).expand_path @base_dir + @outputdir = Pathname.new(@options.op_dir).expand_path @base_dir - return unless @store + return unless @store - refresh_store_data - end + refresh_store_data + end - ## - # Refreshes the generator's data from the store. Called by #setup and - # can be called again after the store has been updated (e.g. in server - # mode after re-parsing changed files). + ## + # Refreshes the generator's data from the store. Called by #setup and + # can be called again after the store has been updated (e.g. in server + # mode after re-parsing changed files). - def refresh_store_data - @classes = @store.all_classes_and_modules.sort - @files = @store.all_files.sort - @methods = @classes.flat_map { |m| m.method_list }.sort - @modsort = get_sorted_module_list @classes - end + def refresh_store_data + @classes = @store.all_classes_and_modules.sort + @files = @store.all_files.sort + @methods = @classes.flat_map { |m| m.method_list }.sort + @modsort = get_sorted_module_list @classes + end - ## - # Creates a template from its components and the +body_file+. - # - # For backwards compatibility, if +body_file+ contains " @@ -613,216 +615,218 @@ def assemble_template(body_file) #{body} TEMPLATE - end + end - ## - # Renders the ERb contained in +file_name+ relative to the template - # directory and returns the result based on the current context. + ## + # Renders the ERb contained in +file_name+ relative to the template + # directory and returns the result based on the current context. - def render(file_name) - template_file = @template_dir + file_name + def render(file_name) + template_file = @template_dir + file_name - template = template_for template_file, false, RDoc::ERBPartial + template = template_for template_file, false, ERBPartial - template.filename = template_file.to_s + template.filename = template_file.to_s - template.result @context - end + template.result @context + end - ## - # Load and render the erb template in the given +template_file+ and write - # it out to +out_file+. - # - # Both +template_file+ and +out_file+ should be Pathname-like objects. - # - # An io will be yielded which must be captured by binding in the caller. + ## + # Load and render the erb template in the given +template_file+ and write + # it out to +out_file+. + # + # Both +template_file+ and +out_file+ should be Pathname-like objects. + # + # An io will be yielded which must be captured by binding in the caller. - def render_template(template_file, out_file = nil) # :yield: io - io_output = out_file && !@dry_run && @file_output - erb_klass = io_output ? RDoc::ERBIO : ERB + def render_template(template_file, out_file = nil) # :yield: io + io_output = out_file && !@dry_run && @file_output + erb_klass = io_output ? ERBIO : ERB - template = template_for template_file, true, erb_klass + template = template_for template_file, true, erb_klass - if io_output - debug_msg "Outputting to %s" % [out_file.expand_path] + if io_output + debug_msg "Outputting to %s" % [out_file.expand_path] - out_file.dirname.mkpath - out_file.open 'w', 0644 do |io| - io.set_encoding @options.encoding + out_file.dirname.mkpath + out_file.open 'w', 0644 do |io| + io.set_encoding @options.encoding - @context = yield io + @context = yield io - template_result template, @context, template_file - end - else - @context = yield nil + template_result template, @context, template_file + end + else + @context = yield nil - output = template_result template, @context, template_file + output = template_result template, @context, template_file - debug_msg " would have written %d characters to %s" % [ - output.length, out_file.expand_path - ] if @dry_run + debug_msg " would have written %d characters to %s" % [ + output.length, out_file.expand_path + ] if @dry_run - output - end - end + output + end + end - ## - # Creates the result for +template+ with +context+. If an error is raised a - # Pathname +template_file+ will indicate the file where the error occurred. - - def template_result(template, context, template_file) - template.filename = template_file.to_s - template.result context - rescue NoMethodError => e - raise RDoc::Error, "Error while evaluating %s: %s" % [ - template_file.expand_path, - e.message, - ], e.backtrace - end + ## + # Creates the result for +template+ with +context+. If an error is raised a + # Pathname +template_file+ will indicate the file where the error occurred. + + def template_result(template, context, template_file) + template.filename = template_file.to_s + template.result context + rescue NoMethodError => e + raise Error, "Error while evaluating %s: %s" % [ + template_file.expand_path, + e.message, + ], e.backtrace + end - ## - # Retrieves a cache template for +file+, if present, or fills the cache. + ## + # Retrieves a cache template for +file+, if present, or fills the cache. - def template_for(file, page = true, klass = ERB) - template = @template_cache[file] + def template_for(file, page = true, klass = ERB) + template = @template_cache[file] - return template if template + return template if template - if page - template = assemble_template file - erbout = 'io' - else - template = file.read(encoding: template_encoding) - template = template.encode @options.encoding + if page + template = assemble_template file + erbout = 'io' + else + template = file.read(encoding: template_encoding) + template = template.encode @options.encoding - file_var = File.basename(file).sub(/\..*/, '') + file_var = File.basename(file).sub(/\..*/, '') - erbout = "_erbout_#{file_var}" - end + erbout = "_erbout_#{file_var}" + end - template = klass.new template, trim_mode: '-', eoutvar: erbout - @template_cache[file] = template - template - end + template = klass.new template, trim_mode: '-', eoutvar: erbout + @template_cache[file] = template + template + end - # :stopdoc: - ParagraphExcerptRegexpOther = %r[\b\w[^./:]++\.] - # use \p/\P{letter} instead of \w/\W in Unicode - ParagraphExcerptRegexpUnicode = %r[\b\p{letter}[^./:]++\.] - # :startdoc: - - # Returns an excerpt of the comment for usage in meta description tags - def excerpt(comment) - text = case comment - when RDoc::Comment - comment.text - else - comment - end + # :stopdoc: + ParagraphExcerptRegexpOther = %r[\b\w[^./:]++\.] + # use \p/\P{letter} instead of \w/\W in Unicode + ParagraphExcerptRegexpUnicode = %r[\b\p{letter}[^./:]++\.] + # :startdoc: + + # Returns an excerpt of the comment for usage in meta description tags + def excerpt(comment) + text = case comment + when Comment + comment.text + else + comment + end - # Match from a capital letter to the first period, discarding any links, so - # that we don't end up matching badges in the README - pattern = ParagraphExcerptRegexpUnicode - begin - first_paragraph_match = text.match(pattern) - rescue Encoding::CompatibilityError - # The doc is non-ASCII text and encoded in other than Unicode base encodings. - raise if pattern == ParagraphExcerptRegexpOther - pattern = ParagraphExcerptRegexpOther - retry - end - return text[0...150].tr_s("\n", " ").squeeze(" ") unless first_paragraph_match + # Match from a capital letter to the first period, discarding any links, so + # that we don't end up matching badges in the README + pattern = ParagraphExcerptRegexpUnicode + begin + first_paragraph_match = text.match(pattern) + rescue ::Encoding::CompatibilityError + # The doc is non-ASCII text and encoded in other than Unicode base encodings. + raise if pattern == ParagraphExcerptRegexpOther + pattern = ParagraphExcerptRegexpOther + retry + end + return text[0...150].tr_s("\n", " ").squeeze(" ") unless first_paragraph_match - extracted_text = first_paragraph_match[0] - second_paragraph = text.match(pattern, first_paragraph_match.end(0)) - extracted_text << " " << second_paragraph[0] if second_paragraph + extracted_text = first_paragraph_match[0] + second_paragraph = text.match(pattern, first_paragraph_match.end(0)) + extracted_text << " " << second_paragraph[0] if second_paragraph - extracted_text[0...150].tr_s("\n", " ").squeeze(" ") - end + extracted_text[0...150].tr_s("\n", " ").squeeze(" ") + end - def generate_ancestor_list(ancestors, klass) - return '' if ancestors.empty? + def generate_ancestor_list(ancestors, klass) + return '' if ancestors.empty? - ancestor = ancestors.shift - content = +'' + end - def generate_class_link(klass, rel_prefix) - if klass.display? - %(
- ' + ancestor = ancestors.shift + content = +'
' - end + content << '
- ' - if ancestor.is_a?(RDoc::NormalClass) - content << "#{ancestor.full_name}" - else - content << ancestor.to_s - end + if ancestor.is_a?(NormalClass) + content << "#{ancestor.full_name}" + else + content << ancestor.to_s + end - # Recursively call the method for the remaining ancestors - content << generate_ancestor_list(ancestors, klass) + # Recursively call the method for the remaining ancestors + content << generate_ancestor_list(ancestors, klass) - content << '
#{klass.name}) - else - %(#{klass.name}) - end - end + def generate_class_link(klass, rel_prefix) + if klass.display? + %(#{klass.name}) + else + %(#{klass.name}) + end + end - def generate_class_index_content(classes, rel_prefix) - grouped_classes = group_classes_by_namespace_for_sidebar(classes) - return '' unless top = grouped_classes[nil] + def generate_class_index_content(classes, rel_prefix) + grouped_classes = group_classes_by_namespace_for_sidebar(classes) + return '' unless top = grouped_classes[nil] - solo = top.one? { |klass| klass.display? } - traverse_classes(top, grouped_classes, rel_prefix, solo) - end + solo = top.one? { |klass| klass.display? } + traverse_classes(top, grouped_classes, rel_prefix, solo) + end - def traverse_classes(klasses, grouped_classes, rel_prefix, solo = false) - content = +'' + def traverse_classes(klasses, grouped_classes, rel_prefix, solo = false) + content = +'
" - end - def group_classes_by_namespace_for_sidebar(classes) - grouped_classes = classes.group_by do |klass| - klass.full_name[/\A[^:]++(?:::[^:]++(?=::))*+(?=::[^:]*+\z)/] - end.select do |_, klasses| - klasses.any?(&:display?) - end + def group_classes_by_namespace_for_sidebar(classes) + grouped_classes = classes.group_by do |klass| + klass.full_name[/\A[^:]++(?:::[^:]++(?=::))*+(?=::[^:]*+\z)/] + end.select do |_, klasses| + klasses.any?(&:display?) + end - grouped_classes.values.each(&:uniq!) - grouped_classes - end + grouped_classes.values.each(&:uniq!) + grouped_classes + end - private + private - def template_encoding - nil - end + def template_encoding + nil + end - def nesting_namespaces_to_class_modules(klass) - tree = {} + def nesting_namespaces_to_class_modules(klass) + tree = {} - klass.nesting_namespaces.zip(klass.fully_qualified_nesting_namespaces) do |ns, fqns| - tree[ns] = @store.classes_hash[fqns] || @store.modules_hash[fqns] - end + klass.nesting_namespaces.zip(klass.fully_qualified_nesting_namespaces) do |ns, fqns| + tree[ns] = @store.classes_hash[fqns] || @store.modules_hash[fqns] + end - tree - end + tree + end - def generate_nesting_namespaces_breadcrumb(klass, rel_prefix) - nesting_namespaces_to_class_modules(klass).map do |namespace, class_module| - path = class_module ? (rel_prefix + class_module.path).to_s : "" - { name: namespace, path: path, self: klass.full_name == class_module&.full_name } + def generate_nesting_namespaces_breadcrumb(klass, rel_prefix) + nesting_namespaces_to_class_modules(klass).map do |namespace, class_module| + path = class_module ? (rel_prefix + class_module.path).to_s : "" + { name: namespace, path: path, self: klass.full_name == class_module&.full_name } + end + end end end end diff --git a/lib/rdoc/generator/json_index.rb b/lib/rdoc/generator/json_index.rb index 402634baf0..cd4ea688cd 100644 --- a/lib/rdoc/generator/json_index.rb +++ b/lib/rdoc/generator/json_index.rb @@ -5,280 +5,284 @@ rescue LoadError end -## -# The JsonIndex generator is designed to complement an HTML generator and -# produces a JSON search index. This generator is derived from sdoc by -# Vladimir Kolesnikov and contains verbatim code written by him. -# -# This generator is designed to be used with a regular HTML generator: -# -# class RDoc::Generator::Darkfish -# def initialize options -# # ... -# @base_dir = Pathname.pwd.expand_path -# -# @json_index = RDoc::Generator::JsonIndex.new self, options -# end -# -# def generate -# # ... -# @json_index.generate -# end -# end -# -# == Index Format -# -# The index is output as a JSON file assigned to the global variable -# +search_data+. The structure is: -# -# var search_data = { -# "index": { -# "searchIndex": -# ["a", "b", ...], -# "longSearchIndex": -# ["a", "a::b", ...], -# "info": [ -# ["A", "A", "A.html", "", ""], -# ["B", "A::B", "A::B.html", "", ""], -# ... -# ] -# } -# } -# -# The same item is described across the +searchIndex+, +longSearchIndex+ and -# +info+ fields. The +searchIndex+ field contains the item's short name, the -# +longSearchIndex+ field contains the full_name (when appropriate) and the -# +info+ field contains the item's name, full_name, path, parameters and a -# snippet of the item's comment. -# -# == LICENSE -# -# Copyright (c) 2009 Vladimir Kolesnikov -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -class RDoc::Generator::JsonIndex - - include RDoc::Text - - ## - # Where the search index lives in the generated output - - SEARCH_INDEX_FILE = File.join 'js', 'search_index.js' - - attr_reader :index # :nodoc: - - ## - # Creates a new generator. - # +options+ are the same options passed to the parent generator. - - def initialize(parent_generator, options) - @parent_generator = parent_generator - @store = parent_generator.store - @options = options - - @template_dir = File.expand_path '../template/json_index', __FILE__ - @base_dir = @parent_generator.base_dir - - @classes = nil - @files = nil - @index = nil - end +module RDoc + module Generator + ## + # The JsonIndex generator is designed to complement an HTML generator and + # produces a JSON search index. This generator is derived from sdoc by + # Vladimir Kolesnikov and contains verbatim code written by him. + # + # This generator is designed to be used with a regular HTML generator: + # + # class RDoc::Generator::Darkfish + # def initialize options + # # ... + # @base_dir = Pathname.pwd.expand_path + # + # @json_index = RDoc::Generator::JsonIndex.new self, options + # end + # + # def generate + # # ... + # @json_index.generate + # end + # end + # + # == Index Format + # + # The index is output as a JSON file assigned to the global variable + # +search_data+. The structure is: + # + # var search_data = { + # "index": { + # "searchIndex": + # ["a", "b", ...], + # "longSearchIndex": + # ["a", "a::b", ...], + # "info": [ + # ["A", "A", "A.html", "", ""], + # ["B", "A::B", "A::B.html", "", ""], + # ... + # ] + # } + # } + # + # The same item is described across the +searchIndex+, +longSearchIndex+ and + # +info+ fields. The +searchIndex+ field contains the item's short name, the + # +longSearchIndex+ field contains the full_name (when appropriate) and the + # +info+ field contains the item's name, full_name, path, parameters and a + # snippet of the item's comment. + # + # == LICENSE + # + # Copyright (c) 2009 Vladimir Kolesnikov + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be + # included in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + class JsonIndex + + include Text + + ## + # Where the search index lives in the generated output + + SEARCH_INDEX_FILE = File.join 'js', 'search_index.js' + + attr_reader :index # :nodoc: + + ## + # Creates a new generator. + # +options+ are the same options passed to the parent generator. + + def initialize(parent_generator, options) + @parent_generator = parent_generator + @store = parent_generator.store + @options = options + + @template_dir = File.expand_path '../template/json_index', __FILE__ + @base_dir = @parent_generator.base_dir + + @classes = nil + @files = nil + @index = nil + end - ## - # Builds the JSON index as a Hash. + ## + # Builds the JSON index as a Hash. - def build_index - reset @store.all_files.sort, @store.all_classes_and_modules.sort + def build_index + reset @store.all_files.sort, @store.all_classes_and_modules.sort - index_classes - index_methods - index_pages + index_classes + index_methods + index_pages - { :index => @index } - end + { :index => @index } + end - ## - # Output progress information if debugging is enabled + ## + # Output progress information if debugging is enabled - def debug_msg(*msg) - return unless $DEBUG_RDOC - $stderr.puts(*msg) - end + def debug_msg(*msg) + return unless $DEBUG_RDOC + $stderr.puts(*msg) + end - ## - # Writes the JSON index to disk + ## + # Writes the JSON index to disk - def generate - debug_msg "Generating JSON index" + def generate + debug_msg "Generating JSON index" - debug_msg " writing search index to %s" % SEARCH_INDEX_FILE - data = build_index + debug_msg " writing search index to %s" % SEARCH_INDEX_FILE + data = build_index - return if @options.dry_run + return if @options.dry_run - out_dir = @base_dir + @options.op_dir - index_file = out_dir + SEARCH_INDEX_FILE + out_dir = @base_dir + @options.op_dir + index_file = out_dir + SEARCH_INDEX_FILE - FileUtils.mkdir_p index_file.dirname, :verbose => $DEBUG_RDOC + FileUtils.mkdir_p index_file.dirname, :verbose => $DEBUG_RDOC - index_file.open 'w', 0644 do |io| - io.set_encoding Encoding::UTF_8 - io.write 'var search_data = ' + index_file.open 'w', 0644 do |io| + io.set_encoding ::Encoding::UTF_8 + io.write 'var search_data = ' - JSON.dump data, io - end - unless ENV['SOURCE_DATE_EPOCH'].nil? - index_file.utime index_file.atime, Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime - end + JSON.dump data, io + end + unless ENV['SOURCE_DATE_EPOCH'].nil? + index_file.utime index_file.atime, Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime + end - Dir.chdir @template_dir do - Dir['**/*.js'].each do |source| - dest = File.join out_dir, source + Dir.chdir @template_dir do + Dir['**/*.js'].each do |source| + dest = File.join out_dir, source - FileUtils.install source, dest, :mode => 0644, :preserve => true, :verbose => $DEBUG_RDOC + FileUtils.install source, dest, :mode => 0644, :preserve => true, :verbose => $DEBUG_RDOC + end + end end - end - end - ## - # Compress the search_index.js file using gzip + ## + # Compress the search_index.js file using gzip - def generate_gzipped - return if @options.dry_run or not defined?(Zlib) + def generate_gzipped + return if @options.dry_run or not defined?(Zlib) - debug_msg "Compressing generated JSON index" - out_dir = @base_dir + @options.op_dir + debug_msg "Compressing generated JSON index" + out_dir = @base_dir + @options.op_dir - search_index_file = out_dir + SEARCH_INDEX_FILE - outfile = out_dir + "#{search_index_file}.gz" + search_index_file = out_dir + SEARCH_INDEX_FILE + outfile = out_dir + "#{search_index_file}.gz" - debug_msg "Reading the JSON index file from %s" % search_index_file - search_index = search_index_file.read(mode: 'r:utf-8') + debug_msg "Reading the JSON index file from %s" % search_index_file + search_index = search_index_file.read(mode: 'r:utf-8') - debug_msg "Writing gzipped search index to %s" % outfile + debug_msg "Writing gzipped search index to %s" % outfile - Zlib::GzipWriter.open(outfile) do |gz| - gz.mtime = File.mtime(search_index_file) - gz.orig_name = search_index_file.basename.to_s - gz.write search_index - gz.close - end + Zlib::GzipWriter.open(outfile) do |gz| + gz.mtime = File.mtime(search_index_file) + gz.orig_name = search_index_file.basename.to_s + gz.write search_index + gz.close + end - # GZip the rest of the js files - Dir.chdir @template_dir do - Dir['**/*.js'].each do |source| - dest = out_dir + source - outfile = out_dir + "#{dest}.gz" + # GZip the rest of the js files + Dir.chdir @template_dir do + Dir['**/*.js'].each do |source| + dest = out_dir + source + outfile = out_dir + "#{dest}.gz" - debug_msg "Reading the original js file from %s" % dest - data = dest.read + debug_msg "Reading the original js file from %s" % dest + data = dest.read - debug_msg "Writing gzipped file to %s" % outfile + debug_msg "Writing gzipped file to %s" % outfile - Zlib::GzipWriter.open(outfile) do |gz| - gz.mtime = File.mtime(dest) - gz.orig_name = dest.basename.to_s - gz.write data - gz.close + Zlib::GzipWriter.open(outfile) do |gz| + gz.mtime = File.mtime(dest) + gz.orig_name = dest.basename.to_s + gz.write data + gz.close + end + end end end - end - end - ## - # Adds classes and modules to the index + ## + # Adds classes and modules to the index - def index_classes - debug_msg " generating class search index" + def index_classes + debug_msg " generating class search index" - documented = @classes.uniq.select do |klass| - klass.document_self_or_methods - end + documented = @classes.uniq.select do |klass| + klass.document_self_or_methods + end - documented.each do |klass| - debug_msg " #{klass.full_name}" - record = klass.search_record - @index[:searchIndex] << search_string(record.shift) - @index[:longSearchIndex] << search_string(record.shift) - @index[:info] << record - end - end + documented.each do |klass| + debug_msg " #{klass.full_name}" + record = klass.search_record + @index[:searchIndex] << search_string(record.shift) + @index[:longSearchIndex] << search_string(record.shift) + @index[:info] << record + end + end - ## - # Adds methods to the index + ## + # Adds methods to the index - def index_methods - debug_msg " generating method search index" + def index_methods + debug_msg " generating method search index" - list = @classes.uniq.flat_map do |klass| - klass.method_list - end.sort_by do |method| - [method.name, method.parent.full_name] - end + list = @classes.uniq.flat_map do |klass| + klass.method_list + end.sort_by do |method| + [method.name, method.parent.full_name] + end - list.each do |method| - debug_msg " #{method.full_name}" - record = method.search_record - @index[:searchIndex] << "#{search_string record.shift}()" - @index[:longSearchIndex] << "#{search_string record.shift}()" - @index[:info] << record - end - end + list.each do |method| + debug_msg " #{method.full_name}" + record = method.search_record + @index[:searchIndex] << "#{search_string record.shift}()" + @index[:longSearchIndex] << "#{search_string record.shift}()" + @index[:info] << record + end + end - ## - # Adds pages to the index + ## + # Adds pages to the index - def index_pages - debug_msg " generating pages search index" + def index_pages + debug_msg " generating pages search index" - pages = @files.select do |file| - file.text? && file.full_name != @options.main_page - end + pages = @files.select do |file| + file.text? && file.full_name != @options.main_page + end - pages.each do |page| - debug_msg " #{page.page_name}" - record = page.search_record - @index[:searchIndex] << search_string(record.shift) - @index[:longSearchIndex] << '' - record.shift - @index[:info] << record - end - end + pages.each do |page| + debug_msg " #{page.page_name}" + record = page.search_record + @index[:searchIndex] << search_string(record.shift) + @index[:longSearchIndex] << '' + record.shift + @index[:info] << record + end + end - def reset(files, classes) # :nodoc: - @files = files - @classes = classes + def reset(files, classes) # :nodoc: + @files = files + @classes = classes - @index = { - :searchIndex => [], - :longSearchIndex => [], - :info => [] - } - end + @index = { + :searchIndex => [], + :longSearchIndex => [], + :info => [] + } + end - ## - # Removes whitespace and downcases +string+ + ## + # Removes whitespace and downcases +string+ - def search_string(string) - string.downcase.gsub(/\s/, '') - end + def search_string(string) + string.downcase.gsub(/\s/, '') + end + end + end end diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb index 38e3251932..944cd1c1a3 100644 --- a/lib/rdoc/generator/markup.rb +++ b/lib/rdoc/generator/markup.rb @@ -1,196 +1,214 @@ # frozen_string_literal: true -## -# Handle common RDoc::Markup tasks for various CodeObjects -# -# This module is loaded by generators. It allows RDoc's CodeObject tree to -# avoid loading generator code to improve startup time for +ri+. +module RDoc + module Generator + ## + # Handle common RDoc::Markup tasks for various CodeObjects + # + # This module is loaded by generators. It allows RDoc's CodeObject tree to + # avoid loading generator code to improve startup time for +ri+. + + module Markup + + ## + # Generates a relative URL from this object's path to +target_path+ + + def aref_to(target_path) + ::RDoc::Markup::ToHtml.gen_relative_url path, target_path + end + + ## + # Generates a relative URL from +from_path+ to this object's path + + def as_href(from_path) + ::RDoc::Markup::ToHtml.gen_relative_url from_path, path + end + + ## + # Handy wrapper for marking up this object's comment + + def description + markup @comment + end + + ## + # Creates an RDoc::Markup::ToHtmlCrossref formatter + + def formatter + return @formatter if defined? @formatter + + options = @store.options + this = Context === self ? self : @parent + + @formatter = ::RDoc::Markup::ToHtmlCrossref.new( + this.path, this, + pipe: options.pipe, + output_decoration: options.output_decoration, + hyperlink_all: options.hyperlink_all, + show_hash: options.show_hash, + autolink_excluded_words: options.autolink_excluded_words || [], + warn_missing_rdoc_ref: options.warn_missing_rdoc_ref + ) + @formatter.code_object = self + @formatter + end + + ## + # Build a webcvs URL starting for the given +url+ with +full_path+ appended + # as the destination path. If +url+ contains '%s' +full_path+ will be + # will replace the %s using sprintf on the +url+. + + def cvs_url(url, full_path) + if /%s/ =~ url + sprintf url, full_path + else + url + full_path + end + end + + ## + # The preferred URL for this object. + + def canonical_url + options = @store.options + if path + File.join(options.canonical_root, path.to_s) + else + options.canonical_root + end + end -module RDoc::Generator::Markup - - ## - # Generates a relative URL from this object's path to +target_path+ - - def aref_to(target_path) - RDoc::Markup::ToHtml.gen_relative_url path, target_path - end - - ## - # Generates a relative URL from +from_path+ to this object's path - - def as_href(from_path) - RDoc::Markup::ToHtml.gen_relative_url from_path, path - end - - ## - # Handy wrapper for marking up this object's comment - - def description - markup @comment - end - - ## - # Creates an RDoc::Markup::ToHtmlCrossref formatter - - def formatter - return @formatter if defined? @formatter - - options = @store.options - this = RDoc::Context === self ? self : @parent - - @formatter = RDoc::Markup::ToHtmlCrossref.new( - this.path, this, - pipe: options.pipe, - output_decoration: options.output_decoration, - hyperlink_all: options.hyperlink_all, - show_hash: options.show_hash, - autolink_excluded_words: options.autolink_excluded_words || [], - warn_missing_rdoc_ref: options.warn_missing_rdoc_ref - ) - @formatter.code_object = self - @formatter - end - - ## - # Build a webcvs URL starting for the given +url+ with +full_path+ appended - # as the destination path. If +url+ contains '%s' +full_path+ will be - # will replace the %s using sprintf on the +url+. - - def cvs_url(url, full_path) - if /%s/ =~ url - sprintf url, full_path - else - url + full_path - end - end - - ## - # The preferred URL for this object. - - def canonical_url - options = @store.options - if path - File.join(options.canonical_root, path.to_s) - else - options.canonical_root end end - end -class RDoc::CodeObject +module RDoc + class CodeObject - include RDoc::Generator::Markup + include Generator::Markup + end end -class RDoc::AnyMethod +module RDoc + class AnyMethod - ## - # Creates an HTML link to the superclass method called by this method. + ## + # Creates an HTML link to the superclass method called by this method. - def superclass_method_link - target = superclass_method - return unless target + def superclass_method_link + target = superclass_method + return unless target - html_formatter = formatter - name = target.full_name + html_formatter = formatter + name = target.full_name - html_formatter.link name, html_formatter.convert_string(name) - end + html_formatter.link name, html_formatter.convert_string(name) + end + end end -class RDoc::MethodAttr +module RDoc + class MethodAttr - ## - # Prepend +src+ with line numbers. + ## + # Prepend +src+ with line numbers. - def add_line_numbers(src) - return if src.empty? || !line - start_line = line - end_line = start_line + src.count("\n") - number_digits = end_line.to_s.length + def add_line_numbers(src) + return if src.empty? || !line + start_line = line + end_line = start_line + src.count("\n") + number_digits = end_line.to_s.length - current_line = start_line - src.gsub!(/^/) do - res = "#{current_line.to_s.rjust(number_digits)} " + current_line = start_line + src.gsub!(/^/) do + res = "#{current_line.to_s.rjust(number_digits)} " - current_line += 1 - res + current_line += 1 + res + end end - end - ## - # Prepend +src+ with a comment that declares its location in the source. + ## + # Prepend +src+ with a comment that declares its location in the source. - def add_location_comment(src) - path = CGI.escapeHTML(file.relative_name) - if options.line_numbers && !src.empty? - src.prepend("# File #{path}\n") - else - src.prepend("# File #{path}, line #{line}\n") + def add_location_comment(src) + path = CGI.escapeHTML(file.relative_name) + if options.line_numbers && !src.empty? + src.prepend("# File #{path}\n") + else + src.prepend("# File #{path}, line #{line}\n") + end end - end - ## - # Turns the method's token stream into HTML. - # - # Prepends line numbers if +options.line_numbers+ is true. + ## + # Turns the method's token stream into HTML. + # + # Prepends line numbers if +options.line_numbers+ is true. - def markup_code - return '' if !@token_stream + def markup_code + return '' if !@token_stream - src = RDoc::TokenStream.to_html @token_stream + src = TokenStream.to_html @token_stream - # dedent the source - common_indent = src.length - src.scan(/^ *(?=\S)/) do |whitespace| - common_indent = whitespace.length if whitespace.length < common_indent - break if common_indent == 0 - end - src.gsub!(/^#{' ' * common_indent}/, '') if common_indent > 0 + # dedent the source + common_indent = src.length + src.scan(/^ *(?=\S)/) do |whitespace| + common_indent = whitespace.length if whitespace.length < common_indent + break if common_indent == 0 + end + src.gsub!(/^#{' ' * common_indent}/, '') if common_indent > 0 + + if source_language == 'ruby' + add_line_numbers(src) if options.line_numbers + add_location_comment(src) + end - if source_language == 'ruby' - add_line_numbers(src) if options.line_numbers - add_location_comment(src) + src end - src end - end -class RDoc::ClassModule +module RDoc + class ClassModule - ## - # Handy wrapper for marking up this class or module's comment + ## + # Handy wrapper for marking up this class or module's comment - def description - markup @comment_location - end + def description + markup @comment_location + end + end end -class RDoc::Context::Section +module RDoc + class Context + class Section - include RDoc::Generator::Markup + include Generator::Markup + end + end end -class RDoc::TopLevel +module RDoc + class TopLevel - ## - # Returns a URL for this source file on some web repository. Use the -W - # command line option to set. + ## + # Returns a URL for this source file on some web repository. Use the -W + # command line option to set. - def cvs_url - url = @store.options.webcvs + def cvs_url + url = @store.options.webcvs - if /%s/ =~ url - url % @relative_name - else - url + @relative_name + if /%s/ =~ url + url % @relative_name + else + url + @relative_name + end end - end + end end diff --git a/lib/rdoc/generator/pot.rb b/lib/rdoc/generator/pot.rb index a20fde077b..5f8d7ee35d 100644 --- a/lib/rdoc/generator/pot.rb +++ b/lib/rdoc/generator/pot.rb @@ -1,94 +1,98 @@ # frozen_string_literal: true -## -# Generates a POT file. -# -# Here is a translator work flow with the generator. -# -# == Create .pot -# -# You create .pot file by pot formatter: -# -# % rdoc --format pot -# -# It generates doc/rdoc.pot. -# -# == Create .po -# -# You create .po file from doc/rdoc.pot. This operation is needed only -# the first time. This work flow assumes that you are a translator -# for Japanese. -# -# You create locale/ja/rdoc.po from doc/rdoc.pot. You can use msginit -# provided by GNU gettext or rmsginit provided by gettext gem. This -# work flow uses gettext gem because it is more portable than GNU -# gettext for Rubyists. Gettext gem is implemented by pure Ruby. -# -# % gem install gettext -# % mkdir -p locale/ja -# % rmsginit --input doc/rdoc.pot --output locale/ja/rdoc.po --locale ja -# -# Translate messages in .po -# -# You translate messages in .po by a PO file editor. po-mode.el exists -# for Emacs users. There are some GUI tools such as GTranslator. -# There are some Web services such as POEditor and Tansifex. You can -# edit by your favorite text editor because .po is a text file. -# Generate localized documentation -# -# You can generate localized documentation with locale/ja/rdoc.po: -# -# % rdoc --locale ja -# -# You can find documentation in Japanese in doc/. Yay! -# -# == Update translation -# -# You need to update translation when your application is added or -# modified messages. -# -# You can update .po by the following command lines: -# -# % rdoc --format pot -# % rmsgmerge --update locale/ja/rdoc.po doc/rdoc.pot -# -# You edit locale/ja/rdoc.po to translate new messages. +module RDoc + module Generator + ## + # Generates a POT file. + # + # Here is a translator work flow with the generator. + # + # == Create .pot + # + # You create .pot file by pot formatter: + # + # % rdoc --format pot + # + # It generates doc/rdoc.pot. + # + # == Create .po + # + # You create .po file from doc/rdoc.pot. This operation is needed only + # the first time. This work flow assumes that you are a translator + # for Japanese. + # + # You create locale/ja/rdoc.po from doc/rdoc.pot. You can use msginit + # provided by GNU gettext or rmsginit provided by gettext gem. This + # work flow uses gettext gem because it is more portable than GNU + # gettext for Rubyists. Gettext gem is implemented by pure Ruby. + # + # % gem install gettext + # % mkdir -p locale/ja + # % rmsginit --input doc/rdoc.pot --output locale/ja/rdoc.po --locale ja + # + # Translate messages in .po + # + # You translate messages in .po by a PO file editor. po-mode.el exists + # for Emacs users. There are some GUI tools such as GTranslator. + # There are some Web services such as POEditor and Tansifex. You can + # edit by your favorite text editor because .po is a text file. + # Generate localized documentation + # + # You can generate localized documentation with locale/ja/rdoc.po: + # + # % rdoc --locale ja + # + # You can find documentation in Japanese in doc/. Yay! + # + # == Update translation + # + # You need to update translation when your application is added or + # modified messages. + # + # You can update .po by the following command lines: + # + # % rdoc --format pot + # % rmsgmerge --update locale/ja/rdoc.po doc/rdoc.pot + # + # You edit locale/ja/rdoc.po to translate new messages. -class RDoc::Generator::POT + class POT - RDoc::RDoc.add_generator self + RDoc.add_generator self - ## - # Description of this generator + ## + # Description of this generator - DESCRIPTION = 'creates .pot file' + DESCRIPTION = 'creates .pot file' - ## - # Set up a new .pot generator + ## + # Set up a new .pot generator - def initialize(store, options) #:not-new: - @options = options - @store = store - end + def initialize(store, options) #:not-new: + @options = options + @store = store + end - ## - # Writes .pot to disk. + ## + # Writes .pot to disk. - def generate - po = extract_messages - pot_path = 'rdoc.pot' - File.open(pot_path, "w") do |pot| - pot.print(po.to_s) - end - end + def generate + po = extract_messages + pot_path = 'rdoc.pot' + File.open(pot_path, "w") do |pot| + pot.print(po.to_s) + end + end - private - def extract_messages - extractor = MessageExtractor.new(@store) - extractor.extract - end + private + def extract_messages + extractor = MessageExtractor.new(@store) + extractor.extract + end - require_relative 'pot/message_extractor' - require_relative 'pot/po' - require_relative 'pot/po_entry' + require_relative 'pot/message_extractor' + require_relative 'pot/po' + require_relative 'pot/po_entry' + end + end end diff --git a/lib/rdoc/generator/pot/message_extractor.rb b/lib/rdoc/generator/pot/message_extractor.rb index ee6d847bd6..c15ca81f3a 100644 --- a/lib/rdoc/generator/pot/message_extractor.rb +++ b/lib/rdoc/generator/pot/message_extractor.rb @@ -1,68 +1,74 @@ # frozen_string_literal: true -## -# Extracts message from RDoc::Store +module RDoc + module Generator + class POT + ## + # Extracts message from RDoc::Store -class RDoc::Generator::POT::MessageExtractor + class MessageExtractor - ## - # Creates a message extractor for +store+. + ## + # Creates a message extractor for +store+. - def initialize(store) - @store = store - @po = RDoc::Generator::POT::PO.new - end + def initialize(store) + @store = store + @po = Generator::POT::PO.new + end - ## - # Extracts messages from +store+, stores them into - # RDoc::Generator::POT::PO and returns it. + ## + # Extracts messages from +store+, stores them into + # RDoc::Generator::POT::PO and returns it. - def extract - @store.all_classes_and_modules.each do |klass| - extract_from_klass(klass) - end - @po - end + def extract + @store.all_classes_and_modules.each do |klass| + extract_from_klass(klass) + end + @po + end - private + private - def extract_from_klass(klass) - extract_text(klass.comment_location, klass.full_name) + def extract_from_klass(klass) + extract_text(klass.comment_location, klass.full_name) - klass.each_section do |section, constants, attributes| - extract_text(section.title, "#{klass.full_name}: section title") - section.comments.each do |comment| - extract_text(comment, "#{klass.full_name}: #{section.title}") - end - end + klass.each_section do |section, constants, attributes| + extract_text(section.title, "#{klass.full_name}: section title") + section.comments.each do |comment| + extract_text(comment, "#{klass.full_name}: #{section.title}") + end + end - klass.constants.each do |constant| - extract_text(constant.comment, constant.full_name) - end + klass.constants.each do |constant| + extract_text(constant.comment, constant.full_name) + end - klass.attributes.each do |attribute| - extract_text(attribute.comment, attribute.full_name) - end + klass.attributes.each do |attribute| + extract_text(attribute.comment, attribute.full_name) + end - klass.each_method do |method| - extract_text(method.comment, method.full_name) - end - end + klass.each_method do |method| + extract_text(method.comment, method.full_name) + end + end - def extract_text(text, comment, location = nil) - return if text.nil? + def extract_text(text, comment, location = nil) + return if text.nil? - options = { - :extracted_comment => comment, - :references => [location].compact, - } - i18n_text = RDoc::I18n::Text.new(text) - i18n_text.extract_messages do |part| - @po.add(entry(part[:paragraph], options)) - end - end + options = { + :extracted_comment => comment, + :references => [location].compact, + } + i18n_text = I18n::Text.new(text) + i18n_text.extract_messages do |part| + @po.add(entry(part[:paragraph], options)) + end + end - def entry(msgid, options) - RDoc::Generator::POT::POEntry.new(msgid, options) - end + def entry(msgid, options) + Generator::POT::POEntry.new(msgid, options) + end + end + end + end end diff --git a/lib/rdoc/generator/pot/po.rb b/lib/rdoc/generator/pot/po.rb index 401025f6e8..000f69c65e 100644 --- a/lib/rdoc/generator/pot/po.rb +++ b/lib/rdoc/generator/pot/po.rb @@ -1,50 +1,53 @@ # frozen_string_literal: true -## -# Generates a PO format text +module RDoc + module Generator + class POT + ## + # Generates a PO format text -class RDoc::Generator::POT::PO + class PO - ## - # Creates an object that represents PO format. + ## + # Creates an object that represents PO format. - def initialize - @entries = {} - add_header - end + def initialize + @entries = {} + add_header + end - ## - # Adds a PO entry to the PO. + ## + # Adds a PO entry to the PO. - def add(entry) - existing_entry = @entries[entry.msgid] - if existing_entry - entry = existing_entry.merge(entry) - end - @entries[entry.msgid] = entry - end + def add(entry) + existing_entry = @entries[entry.msgid] + if existing_entry + entry = existing_entry.merge(entry) + end + @entries[entry.msgid] = entry + end - ## - # Returns PO format text for the PO. + ## + # Returns PO format text for the PO. - def to_s - sort_entries.map(&:to_s).join("\n") - end + def to_s + sort_entries.map(&:to_s).join("\n") + end - private + private - def add_header - add(header_entry) - end + def add_header + add(header_entry) + end - def header_entry - comment = <<-COMMENT + def header_entry + comment = <<-COMMENT SOME DESCRIPTIVE TITLE. Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER This file is distributed under the same license as the PACKAGE package. FIRST AUTHOR' + + klasses.each do |index_klass| + if children = grouped_classes[index_klass.full_name] + content << %(
" end - end - - "#{content}- ' + solo = false + elsif index_klass.display? + content << %(
#{generate_class_link(index_klass, rel_prefix)}
) + content << traverse_classes(children, grouped_classes, rel_prefix) + content << '- #{generate_class_link(index_klass, rel_prefix)}
) + end + end - klasses.each do |index_klass| - if children = grouped_classes[index_klass.full_name] - content << %(- ' - solo = false - elsif index_klass.display? - content << %(
#{generate_class_link(index_klass, rel_prefix)}
) - content << traverse_classes(children, grouped_classes, rel_prefix) - content << '- #{generate_class_link(index_klass, rel_prefix)}
) + "#{content}, YEAR. COMMENT - content = <<-CONTENT + content = <<-CONTENT Project-Id-Version: PACKAGE VERSEION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO_DA HO:MI+ZONE @@ -57,23 +60,26 @@ def header_entry Plural-Forms: nplurals=INTEGER; plural=EXPRESSION; CONTENT - options = { - :msgstr => content, - :translator_comment => comment, - :flags => ['fuzzy'], - } - RDoc::Generator::POT::POEntry.new('', options) - end + options = { + :msgstr => content, + :translator_comment => comment, + :flags => ['fuzzy'], + } + Generator::POT::POEntry.new('', options) + end - def sort_entries - headers, messages = @entries.values.partition do |entry| - entry.msgid.empty? - end - # TODO: sort by location - sorted_messages = messages.sort_by do |entry| - entry.msgid + def sort_entries + headers, messages = @entries.values.partition do |entry| + entry.msgid.empty? + end + # TODO: sort by location + sorted_messages = messages.sort_by do |entry| + entry.msgid + end + headers + sorted_messages + end + + end end - headers + sorted_messages end - end diff --git a/lib/rdoc/generator/pot/po_entry.rb b/lib/rdoc/generator/pot/po_entry.rb index 8de260eef1..e873b57926 100644 --- a/lib/rdoc/generator/pot/po_entry.rb +++ b/lib/rdoc/generator/pot/po_entry.rb @@ -1,141 +1,147 @@ # frozen_string_literal: true -## -# A PO entry in PO - -class RDoc::Generator::POT::POEntry - - # The msgid content - attr_reader :msgid - - # The msgstr content - attr_reader :msgstr - - # The comment content created by translator (PO editor) - attr_reader :translator_comment - - # The comment content extracted from source file - attr_reader :extracted_comment - - # The locations where the PO entry is extracted - attr_reader :references - - # The flags of the PO entry - attr_reader :flags - - ## - # Creates a PO entry for +msgid+. Other values can be specified by - # +options+. - - def initialize(msgid, options = {}) - @msgid = msgid - @msgstr = options[:msgstr] || "" - @translator_comment = options[:translator_comment] - @extracted_comment = options[:extracted_comment] - @references = options[:references] || [] - @flags = options[:flags] || [] - end - - ## - # Returns the PO entry in PO format. - - def to_s - entry = '' - entry += format_translator_comment - entry += format_extracted_comment - entry += format_references - entry += format_flags - entry += <<-ENTRY +module RDoc + module Generator + class POT + ## + # A PO entry in PO + + class POEntry + + # The msgid content + attr_reader :msgid + + # The msgstr content + attr_reader :msgstr + + # The comment content created by translator (PO editor) + attr_reader :translator_comment + + # The comment content extracted from source file + attr_reader :extracted_comment + + # The locations where the PO entry is extracted + attr_reader :references + + # The flags of the PO entry + attr_reader :flags + + ## + # Creates a PO entry for +msgid+. Other values can be specified by + # +options+. + + def initialize(msgid, options = {}) + @msgid = msgid + @msgstr = options[:msgstr] || "" + @translator_comment = options[:translator_comment] + @extracted_comment = options[:extracted_comment] + @references = options[:references] || [] + @flags = options[:flags] || [] + end + + ## + # Returns the PO entry in PO format. + + def to_s + entry = '' + entry += format_translator_comment + entry += format_extracted_comment + entry += format_references + entry += format_flags + entry += <<-ENTRY msgid #{format_message(@msgid)} msgstr #{format_message(@msgstr)} ENTRY - end - - ## - # Merges the PO entry with +other_entry+. - - def merge(other_entry) - options = { - :extracted_comment => merge_string(@extracted_comment, - other_entry.extracted_comment), - :translator_comment => merge_string(@translator_comment, - other_entry.translator_comment), - :references => merge_array(@references, - other_entry.references), - :flags => merge_array(@flags, - other_entry.flags), - } - self.class.new(@msgid, options) - end - - private - - def format_comment(mark, comment) - return '' unless comment - return '' if comment.empty? - - formatted_comment = '' - comment.each_line do |line| - formatted_comment += "#{mark} #{line}" - end - formatted_comment += "\n" unless formatted_comment.end_with?("\n") - formatted_comment - end + end + + ## + # Merges the PO entry with +other_entry+. + + def merge(other_entry) + options = { + :extracted_comment => merge_string(@extracted_comment, + other_entry.extracted_comment), + :translator_comment => merge_string(@translator_comment, + other_entry.translator_comment), + :references => merge_array(@references, + other_entry.references), + :flags => merge_array(@flags, + other_entry.flags), + } + self.class.new(@msgid, options) + end + + private + + def format_comment(mark, comment) + return '' unless comment + return '' if comment.empty? + + formatted_comment = '' + comment.each_line do |line| + formatted_comment += "#{mark} #{line}" + end + formatted_comment += "\n" unless formatted_comment.end_with?("\n") + formatted_comment + end + + def format_translator_comment + format_comment('#', @translator_comment) + end + + def format_extracted_comment + format_comment('#.', @extracted_comment) + end + + def format_references + return '' if @references.empty? + + formatted_references = '' + @references.sort.each do |file, line| + formatted_references += "\#: #{file}:#{line}\n" + end + formatted_references + end + + def format_flags + return '' if @flags.empty? + + formatted_flags = flags.join(",") + "\#, #{formatted_flags}\n" + end + + def format_message(message) + return "\"#{escape(message)}\"" unless message.include?("\n") + + formatted_message = '""' + message.each_line do |line| + formatted_message += "\n" + formatted_message += "\"#{escape(line)}\"" + end + formatted_message + end + + def escape(string) + string.gsub(/["\\\t\n]/) do |special_character| + case special_character + when "\t" + "\\t" + when "\n" + "\\n" + else + "\\#{special_character}" + end + end + end + + def merge_string(string1, string2) + [string1, string2].compact.join("\n") + end + + def merge_array(array1, array2) + (array1 + array2).uniq + end - def format_translator_comment - format_comment('#', @translator_comment) - end - - def format_extracted_comment - format_comment('#.', @extracted_comment) - end - - def format_references - return '' if @references.empty? - - formatted_references = '' - @references.sort.each do |file, line| - formatted_references += "\#: #{file}:#{line}\n" - end - formatted_references - end - - def format_flags - return '' if @flags.empty? - - formatted_flags = flags.join(",") - "\#, #{formatted_flags}\n" - end - - def format_message(message) - return "\"#{escape(message)}\"" unless message.include?("\n") - - formatted_message = '""' - message.each_line do |line| - formatted_message += "\n" - formatted_message += "\"#{escape(line)}\"" - end - formatted_message - end - - def escape(string) - string.gsub(/["\\\t\n]/) do |special_character| - case special_character - when "\t" - "\\t" - when "\n" - "\\n" - else - "\\#{special_character}" end end end - - def merge_string(string1, string2) - [string1, string2].compact.join("\n") - end - - def merge_array(array1, array2) - (array1 + array2).uniq - end - end diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb index 32f518ac71..d1f74b8abe 100644 --- a/lib/rdoc/generator/ri.rb +++ b/lib/rdoc/generator/ri.rb @@ -1,30 +1,34 @@ # frozen_string_literal: true -## -# Generates ri data files +module RDoc + module Generator + ## + # Generates ri data files -class RDoc::Generator::RI + class RI - RDoc::RDoc.add_generator self + RDoc.add_generator self - ## - # Description of this generator + ## + # Description of this generator - DESCRIPTION = 'creates ri data files' + DESCRIPTION = 'creates ri data files' - ## - # Set up a new ri generator + ## + # Set up a new ri generator - def initialize(store, options) #:not-new: - @options = options - @store = store - @store.path = '.' - end + def initialize(store, options) #:not-new: + @options = options + @store = store + @store.path = '.' + end - ## - # Writes the parsed data store to disk for use by ri. + ## + # Writes the parsed data store to disk for use by ri. - def generate - @store.save - end + def generate + @store.save + end + end + end end diff --git a/lib/rdoc/generator/template/aliki/_footer.rhtml b/lib/rdoc/generator/template/aliki/_footer.rhtml index 525d853dec..ae2f831443 100644 --- a/lib/rdoc/generator/template/aliki/_footer.rhtml +++ b/lib/rdoc/generator/template/aliki/_footer.rhtml @@ -16,7 +16,7 @@ diff --git a/lib/rdoc/generator/template/aliki/_head.rhtml b/lib/rdoc/generator/template/aliki/_head.rhtml index c6c238d26c..96ba1d65e5 100644 --- a/lib/rdoc/generator/template/aliki/_head.rhtml +++ b/lib/rdoc/generator/template/aliki/_head.rhtml @@ -112,52 +112,52 @@ <%- @options.template_stylesheets.each do |stylesheet| %> <%- end %> diff --git a/lib/rdoc/generator/template/darkfish/_footer.rhtml b/lib/rdoc/generator/template/darkfish/_footer.rhtml index 620cf01484..3ccaa2d836 100644 --- a/lib/rdoc/generator/template/darkfish/_footer.rhtml +++ b/lib/rdoc/generator/template/darkfish/_footer.rhtml @@ -1,5 +1,5 @@ diff --git a/lib/rdoc/i18n.rb b/lib/rdoc/i18n.rb index f209a9a6f6..2491045f6d 100644 --- a/lib/rdoc/i18n.rb +++ b/lib/rdoc/i18n.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true -## -# This module provides i18n related features. +module RDoc + ## + # This module provides i18n related features. -module RDoc::I18n + module I18n - autoload :Locale, "#{__dir__}/i18n/locale" - require_relative 'i18n/text' + autoload :Locale, "#{__dir__}/i18n/locale" + require_relative 'i18n/text' + end end diff --git a/lib/rdoc/i18n/locale.rb b/lib/rdoc/i18n/locale.rb index 6a70d6c986..3d42ff1ca2 100644 --- a/lib/rdoc/i18n/locale.rb +++ b/lib/rdoc/i18n/locale.rb @@ -1,102 +1,106 @@ # frozen_string_literal: true -## -# A message container for a locale. -# -# This object provides the following two features: -# -# * Loads translated messages from .po file. -# * Translates a message into the locale. - -class RDoc::I18n::Locale - - @@locales = {} # :nodoc: - - class << self - +module RDoc + module I18n ## - # Returns the locale object for +locale_name+. - - def [](locale_name) - @@locales[locale_name] ||= new(locale_name) - end - - ## - # Sets the locale object for +locale_name+. + # A message container for a locale. # - # Normally, this method is not used. This method is useful for - # testing. - - def []=(locale_name, locale) - @@locales[locale_name] = locale - end - - end - - ## - # The name of the locale. It uses IETF language tag format - # +[language[_territory][.codeset][@modifier]]+. - # - # See also {BCP 47 - Tags for Identifying - # Languages}[http://tools.ietf.org/rfc/bcp/bcp47.txt]. - - attr_reader :name - - ## - # Creates a new locale object for +name+ locale. +name+ must - # follow IETF language tag format. - - def initialize(name) - @name = name - @messages = {} - end + # This object provides the following two features: + # + # * Loads translated messages from .po file. + # * Translates a message into the locale. + + class Locale + + @@locales = {} # :nodoc: + + class << self + + ## + # Returns the locale object for +locale_name+. + + def [](locale_name) + @@locales[locale_name] ||= new(locale_name) + end + + ## + # Sets the locale object for +locale_name+. + # + # Normally, this method is not used. This method is useful for + # testing. + + def []=(locale_name, locale) + @@locales[locale_name] = locale + end + + end + + ## + # The name of the locale. It uses IETF language tag format + # +[language[_territory][.codeset][@modifier]]+. + # + # See also {BCP 47 - Tags for Identifying + # Languages}[http://tools.ietf.org/rfc/bcp/bcp47.txt]. + + attr_reader :name + + ## + # Creates a new locale object for +name+ locale. +name+ must + # follow IETF language tag format. + + def initialize(name) + @name = name + @messages = {} + end + + ## + # Loads translation messages from +locale_directory+/+@name+/rdoc.po + # or +locale_directory+/+@name+.po. The former has high priority. + # + # This method requires gettext gem for parsing .po file. If you + # don't have gettext gem, this method doesn't load .po file. This + # method warns and returns +false+. + # + # Returns +true+ if succeeded, +false+ otherwise. + + def load(locale_directory) + return false if @name.nil? + + po_file_candidates = [ + File.join(locale_directory, @name, 'rdoc.po'), + File.join(locale_directory, "#{@name}.po"), + ] + po_file = po_file_candidates.find do |po_file_candidate| + File.exist?(po_file_candidate) + end + return false unless po_file + + begin + require 'gettext/po_parser' + require 'gettext/mo' + rescue LoadError + warn('Need gettext gem for i18n feature:') + warn(' gem install gettext') + return false + end + + po_parser = GetText::POParser.new + messages = GetText::MO.new + po_parser.report_warning = false + po_parser.parse_file(po_file, messages) + + @messages.merge!(messages) + + true + end + + ## + # Translates the +message+ into locale. If there is no translation + # messages for +message+ in locale, +message+ itself is returned. + + def translate(message) + @messages[message] || message + end - ## - # Loads translation messages from +locale_directory+/+@name+/rdoc.po - # or +locale_directory+/+@name+.po. The former has high priority. - # - # This method requires gettext gem for parsing .po file. If you - # don't have gettext gem, this method doesn't load .po file. This - # method warns and returns +false+. - # - # Returns +true+ if succeeded, +false+ otherwise. - - def load(locale_directory) - return false if @name.nil? - - po_file_candidates = [ - File.join(locale_directory, @name, 'rdoc.po'), - File.join(locale_directory, "#{@name}.po"), - ] - po_file = po_file_candidates.find do |po_file_candidate| - File.exist?(po_file_candidate) - end - return false unless po_file - - begin - require 'gettext/po_parser' - require 'gettext/mo' - rescue LoadError - warn('Need gettext gem for i18n feature:') - warn(' gem install gettext') - return false end - - po_parser = GetText::POParser.new - messages = GetText::MO.new - po_parser.report_warning = false - po_parser.parse_file(po_file, messages) - - @messages.merge!(messages) - - true - end - - ## - # Translates the +message+ into locale. If there is no translation - # messages for +message+ in locale, +message+ itself is returned. - - def translate(message) - @messages[message] || message end - end diff --git a/lib/rdoc/i18n/text.rb b/lib/rdoc/i18n/text.rb index d3008e33e3..f4579ffcda 100644 --- a/lib/rdoc/i18n/text.rb +++ b/lib/rdoc/i18n/text.rb @@ -1,126 +1,130 @@ # frozen_string_literal: true -## -# An i18n supported text. -# -# This object provides the following two features: -# -# * Extracts translation messages from wrapped raw text. -# * Translates wrapped raw text in specified locale. -# -# Wrapped raw text is one of String, RDoc::Comment or Array of them. +module RDoc + module I18n + ## + # An i18n supported text. + # + # This object provides the following two features: + # + # * Extracts translation messages from wrapped raw text. + # * Translates wrapped raw text in specified locale. + # + # Wrapped raw text is one of String, RDoc::Comment or Array of them. -class RDoc::I18n::Text + class Text - ## - # Creates a new i18n supported text for +raw+ text. + ## + # Creates a new i18n supported text for +raw+ text. - def initialize(raw) - @raw = raw - end + def initialize(raw) + @raw = raw + end - ## - # Extracts translation target messages and yields each message. - # - # Each yielded message is a Hash. It consists of the followings: - # - # :type :: :paragraph - # :paragraph :: String (The translation target message itself.) - # :line_no :: Integer (The line number of the :paragraph is started.) - # - # The above content may be added in the future. + ## + # Extracts translation target messages and yields each message. + # + # Each yielded message is a Hash. It consists of the followings: + # + # :type :: :paragraph + # :paragraph :: String (The translation target message itself.) + # :line_no :: Integer (The line number of the :paragraph is started.) + # + # The above content may be added in the future. - def extract_messages - parse do |part| - case part[:type] - when :empty_line - # ignore - when :paragraph - yield(part) + def extract_messages + parse do |part| + case part[:type] + when :empty_line + # ignore + when :paragraph + yield(part) + end + end end - end - end - # Translates raw text into +locale+. - def translate(locale) - translated_text = '' - parse do |part| - case part[:type] - when :paragraph - translated_text += locale.translate(part[:paragraph]) - when :empty_line - translated_text += part[:line] - else - raise "should not reach here: unexpected type: #{type}" + # Translates raw text into +locale+. + def translate(locale) + translated_text = '' + parse do |part| + case part[:type] + when :paragraph + translated_text += locale.translate(part[:paragraph]) + when :empty_line + translated_text += part[:line] + else + raise "should not reach here: unexpected type: #{type}" + end + end + translated_text end - end - translated_text - end - private - def parse(&block) - paragraph = '' - paragraph_start_line = 0 - line_no = 0 + private + def parse(&block) + paragraph = '' + paragraph_start_line = 0 + line_no = 0 - each_line(@raw) do |line| - line_no += 1 - case line - when /\A\s*\z/ - if paragraph.empty? - emit_empty_line_event(line, line_no, &block) - else - paragraph += line - emit_paragraph_event(paragraph, paragraph_start_line, line_no, - &block) - paragraph = '' + each_line(@raw) do |line| + line_no += 1 + case line + when /\A\s*\z/ + if paragraph.empty? + emit_empty_line_event(line, line_no, &block) + else + paragraph += line + emit_paragraph_event(paragraph, paragraph_start_line, line_no, + &block) + paragraph = '' + end + else + paragraph_start_line = line_no if paragraph.empty? + paragraph += line + end + end + + unless paragraph.empty? + emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block) end - else - paragraph_start_line = line_no if paragraph.empty? - paragraph += line end - end - unless paragraph.empty? - emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block) - end - end + def each_line(raw, &block) + case raw + when Comment + raw.text.each_line(&block) + when Hash + raw.each_value do |comments| + comments.each { |comment| each_line(comment, &block) } + end + else + raw.each_line(&block) + end + end - def each_line(raw, &block) - case raw - when RDoc::Comment - raw.text.each_line(&block) - when Hash - raw.each_value do |comments| - comments.each { |comment| each_line(comment, &block) } + def emit_empty_line_event(line, line_no) + part = { + :type => :empty_line, + :line => line, + :line_no => line_no, + } + yield(part) end - else - raw.each_line(&block) - end - end - def emit_empty_line_event(line, line_no) - part = { - :type => :empty_line, - :line => line, - :line_no => line_no, - } - yield(part) - end + def emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block) + paragraph_part = { + :type => :paragraph, + :line_no => paragraph_start_line, + } + match_data = /(\s*)\z/.match(paragraph) + if match_data + paragraph_part[:paragraph] = match_data.pre_match + yield(paragraph_part) + emit_empty_line_event(match_data[1], line_no, &block) + else + paragraph_part[:paragraph] = paragraph + yield(paragraph_part) + end + end - def emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block) - paragraph_part = { - :type => :paragraph, - :line_no => paragraph_start_line, - } - match_data = /(\s*)\z/.match(paragraph) - if match_data - paragraph_part[:paragraph] = match_data.pre_match - yield(paragraph_part) - emit_empty_line_event(match_data[1], line_no, &block) - else - paragraph_part[:paragraph] = paragraph - yield(paragraph_part) end end - end diff --git a/lib/rdoc/markdown/byte_runtime.rb b/lib/rdoc/markdown/byte_runtime.rb index 86c09fca3c..ae03e9cdc1 100644 --- a/lib/rdoc/markdown/byte_runtime.rb +++ b/lib/rdoc/markdown/byte_runtime.rb @@ -2,77 +2,79 @@ require 'strscan' -class RDoc::Markdown +module RDoc + class Markdown - ## - # Byte-offset replacements for the position helpers of the kpeg-generated - # parser runtime. - # - # The generated runtime addresses +@string+ by character index, which makes - # every position lookup scan the string from its beginning when the input - # contains non-ASCII characters, so parse time becomes quadratic in the - # input size. Generated rule bodies only save and restore +pos+ without - # inspecting it, so replacing these helpers is enough to switch the whole - # parser to byte offsets. - # - # get_byte (the grammar's `.`) consumes one character and returns its - # codepoint, exactly like the character-index runtime, so positions always - # stay on character boundaries and the only observable difference is the - # position values themselves. The input must be validly encoded in an - # ASCII-compatible encoding. - # - # The error-reporting helpers of the generated runtime (+current_line+, - # +current_column+, ...) are left as-is and would misreport locations when - # given byte offsets. They are unreachable: markdown is deliberately - # designed to parse any input somehow rather than fail (the root rule - # `Doc = BOM? Block*` cannot fail), so a parse failure means a bug in the - # grammar itself, and nothing in RDoc invokes +raise_error+ or - # +show_error+. Make these helpers byte-aware before using them for - # anything. + ## + # Byte-offset replacements for the position helpers of the kpeg-generated + # parser runtime. + # + # The generated runtime addresses +@string+ by character index, which makes + # every position lookup scan the string from its beginning when the input + # contains non-ASCII characters, so parse time becomes quadratic in the + # input size. Generated rule bodies only save and restore +pos+ without + # inspecting it, so replacing these helpers is enough to switch the whole + # parser to byte offsets. + # + # get_byte (the grammar's `.`) consumes one character and returns its + # codepoint, exactly like the character-index runtime, so positions always + # stay on character boundaries and the only observable difference is the + # position values themselves. The input must be validly encoded in an + # ASCII-compatible encoding. + # + # The error-reporting helpers of the generated runtime (+current_line+, + # +current_column+, ...) are left as-is and would misreport locations when + # given byte offsets. They are unreachable: markdown is deliberately + # designed to parse any input somehow rather than fail (the root rule + # `Doc = BOM? Block*` cannot fail), so a parse failure means a bug in the + # grammar itself, and nothing in RDoc invokes +raise_error+ or + # +show_error+. Make these helpers byte-aware before using them for + # anything. - module ByteRuntime - def set_string(string, pos) - @string = string - @string_size = string ? string.bytesize : 0 - @pos = pos - @position_line_offsets = nil - @scanner = string ? StringScanner.new(string) : nil - end + module ByteRuntime + def set_string(string, pos) + @string = string + @string_size = string ? string.bytesize : 0 + @pos = pos + @position_line_offsets = nil + @scanner = string ? StringScanner.new(string) : nil + end - def scan(reg) - @scanner.pos = @pos - if @scanner.skip(reg) - @pos = @scanner.pos - true + def scan(reg) + @scanner.pos = @pos + if @scanner.skip(reg) + @pos = @scanner.pos + true + end end - end - def match_string(str) - len = str.bytesize - if @string.byteslice(@pos, len) == str - @pos += len - str + def match_string(str) + len = str.bytesize + if @string.byteslice(@pos, len) == str + @pos += len + str + end end - end - def get_byte - byte = @string.getbyte(@pos) - return nil unless byte + def get_byte + byte = @string.getbyte(@pos) + return nil unless byte - if byte < 0x80 - @pos += 1 - byte - else - @scanner.pos = @pos - # /./ interprets the character in the string's own encoding - char = @scanner.scan(/./m) - @pos = @scanner.pos - char.ord + if byte < 0x80 + @pos += 1 + byte + else + @scanner.pos = @pos + # /./ interprets the character in the string's own encoding + char = @scanner.scan(/./m) + @pos = @scanner.pos + char.ord + end end - end - def get_text(start) - @string.byteslice(start, @pos - start) + def get_text(start) + @string.byteslice(start, @pos - start) + end end end end diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index 4348e29f27..ea89b4600a 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -1,127 +1,128 @@ # frozen_string_literal: true -## -# RDoc::Markup parses plain text documents and attempts to decompose them into -# their constituent parts. Some of these parts are high-level: paragraphs, -# chunks of verbatim text, list entries and the like. Other parts happen at -# the character level: a piece of bold text, a word in code font. This markup -# is similar in spirit to that used on WikiWiki webs, where folks create web -# pages using a simple set of formatting rules. -# -# RDoc::Markup and other markup formats do no output formatting, this is -# handled by the RDoc::Markup::Formatter subclasses. -# -# = Markup Formats -# -# +RDoc+ supports these markup formats: -# -# - +rdoc+: -# the +RDoc+ markup format; -# see {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc] -# - +markdown+: -# The +markdown+ markup format as described in -# the {Markdown Guide}[https://www.markdownguide.org]; -# see RDoc::Markdown. -# - +rd+: -# the +rd+ markup format format; -# see RDoc::RD. -# - +tomdoc+: -# the TomDoc format as described in -# {TomDoc for Ruby}[http://tomdoc.org]; -# see RDoc::TomDoc. -# -# You can choose a markup format using the following methods: -# -# per project:: -# If you build your documentation with rake use RDoc::Task#markup. -# -# If you build your documentation by hand run: -# -# rdoc --markup your_favorite_format --write-options -# -# and commit .rdoc_options and ship it with your packaged gem. -# per file:: -# At the top of the file use the :markup: directive to set the -# default format for the rest of the file. -# per comment:: -# Use the :markup: directive at the top of a comment you want -# to write in a different format. -# -# = RDoc::Markup -# -# RDoc::Markup is extensible at runtime: you can add \new markup elements to -# be recognized in the documents that RDoc::Markup parses. -# -# RDoc::Markup is intended to be the basis for a family of tools which share -# the common requirement that simple, plain-text should be rendered in a -# variety of different output formats and media. It is envisaged that -# RDoc::Markup could be the basis for formatting RDoc style comment blocks, -# Wiki entries, and online FAQs. -# -# == Synopsis -# -# This code converts +input_string+ to HTML. The conversion takes place in -# the +convert+ method, so you can use the same RDoc::Markup converter to -# convert multiple input strings. -# -# require 'rdoc' -# -# h = RDoc::Markup::ToHtml.new(RDoc::Options.new) -# -# puts h.convert(input_string) -# -# You can extend the RDoc::Markup parser to recognize new markup -# sequences, and to add regexp handling. Here we make WikiWords significant to -# the parser, and also make the sequences {word} and \ text... signify -# strike-through text. We then subclass the HTML output class to deal -# with these: -# -# require 'rdoc' -# -# class WikiHtml < RDoc::Markup::ToHtml -# def handle_regexp_WIKIWORD(target) -# "" + target + "" -# end -# end -# -# markup = RDoc::Markup.new -# markup.add_word_pair("{", "}", :STRIKE) -# markup.add_html("no", :STRIKE) -# -# markup.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) -# -# wh = WikiHtml.new RDoc::Options.new, markup -# wh.add_tag(:STRIKE, "", "") -# -# puts "#{wh.convert ARGF.read}" -# -# == Encoding -# -# Where Encoding support is available, RDoc will automatically convert all -# documents to the same output encoding. The output encoding can be set via -# RDoc::Options#encoding and defaults to Encoding.default_external. -# -# = \RDoc Markup Reference -# -# See {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc] -# -#-- -# Original Author:: Dave Thomas, dave@pragmaticprogrammer.com -# License:: Ruby license - -class RDoc::Markup - - # Array of regexp handling pattern and its name. A regexp handling - # sequence is something like a WikiWord - - attr_reader :regexp_handlings - +module RDoc ## - # Parses +str+ into an RDoc::Markup::Document. + # RDoc::Markup parses plain text documents and attempts to decompose them into + # their constituent parts. Some of these parts are high-level: paragraphs, + # chunks of verbatim text, list entries and the like. Other parts happen at + # the character level: a piece of bold text, a word in code font. This markup + # is similar in spirit to that used on WikiWiki webs, where folks create web + # pages using a simple set of formatting rules. + # + # RDoc::Markup and other markup formats do no output formatting, this is + # handled by the RDoc::Markup::Formatter subclasses. + # + # = Markup Formats + # + # +RDoc+ supports these markup formats: + # + # - +rdoc+: + # the +RDoc+ markup format; + # see {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc] + # - +markdown+: + # The +markdown+ markup format as described in + # the {Markdown Guide}[https://www.markdownguide.org]; + # see RDoc::Markdown. + # - +rd+: + # the +rd+ markup format format; + # see RDoc::RD. + # - +tomdoc+: + # the TomDoc format as described in + # {TomDoc for Ruby}[http://tomdoc.org]; + # see RDoc::TomDoc. + # + # You can choose a markup format using the following methods: + # + # per project:: + # If you build your documentation with rake use RDoc::Task#markup. + # + # If you build your documentation by hand run: + # + # rdoc --markup your_favorite_format --write-options + # + # and commit .rdoc_options and ship it with your packaged gem. + # per file:: + # At the top of the file use the :markup: directive to set the + # default format for the rest of the file. + # per comment:: + # Use the :markup: directive at the top of a comment you want + # to write in a different format. + # + # = RDoc::Markup + # + # RDoc::Markup is extensible at runtime: you can add \new markup elements to + # be recognized in the documents that RDoc::Markup parses. + # + # RDoc::Markup is intended to be the basis for a family of tools which share + # the common requirement that simple, plain-text should be rendered in a + # variety of different output formats and media. It is envisaged that + # RDoc::Markup could be the basis for formatting RDoc style comment blocks, + # Wiki entries, and online FAQs. + # + # == Synopsis + # + # This code converts +input_string+ to HTML. The conversion takes place in + # the +convert+ method, so you can use the same RDoc::Markup converter to + # convert multiple input strings. + # + # require 'rdoc' + # + # h = RDoc::Markup::ToHtml.new(RDoc::Options.new) + # + # puts h.convert(input_string) + # + # You can extend the RDoc::Markup parser to recognize new markup + # sequences, and to add regexp handling. Here we make WikiWords significant to + # the parser, and also make the sequences {word} and \text... signify + # strike-through text. We then subclass the HTML output class to deal + # with these: + # + # require 'rdoc' + # + # class WikiHtml < RDoc::Markup::ToHtml + # def handle_regexp_WIKIWORD(target) + # "" + target + "" + # end + # end + # + # markup = RDoc::Markup.new + # markup.add_word_pair("{", "}", :STRIKE) + # markup.add_html("no", :STRIKE) + # + # markup.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) + # + # wh = WikiHtml.new RDoc::Options.new, markup + # wh.add_tag(:STRIKE, "", "") + # + # puts "#{wh.convert ARGF.read}" + # + # == Encoding + # + # Where Encoding support is available, RDoc will automatically convert all + # documents to the same output encoding. The output encoding can be set via + # RDoc::Options#encoding and defaults to Encoding.default_external. + # + # = \RDoc Markup Reference + # + # See {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc] + # + #-- + # Original Author:: Dave Thomas, dave@pragmaticprogrammer.com + # License:: Ruby license - def self.parse(str) - RDoc::Markup::Parser.parse str - rescue RDoc::Markup::Parser::Error => e - $stderr.puts <<-EOF + class Markup + + # Array of regexp handling pattern and its name. A regexp handling + # sequence is something like a WikiWord + + attr_reader :regexp_handlings + + ## + # Parses +str+ into an RDoc::Markup::Document. + + def self.parse(str) + Markup::Parser.parse str + rescue Markup::Parser::Error => e + $stderr.puts <<-EOF While parsing markup, RDoc encountered a #{e.class}: #{e} @@ -131,7 +132,7 @@ def self.parse(str) #{text} ---8<--- -RDoc #{RDoc::VERSION} +RDoc #{VERSION} Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} #{RUBY_RELEASE_DATE} @@ -140,80 +141,81 @@ def self.parse(str) https://github.com/ruby/rdoc/issues EOF - raise - end - - ## - # Take a block of text and use various heuristics to determine its - # structure (paragraphs, lists, and so on). Invoke an event handler as we - # identify significant chunks. - - def initialize - @regexp_handlings = [] - @output = nil - end - - ## - # Add to other inline sequences. For example, we could add WikiWords using - # something like: - # - # parser.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) - # - # Each wiki word will be presented to the output formatter. - - def add_regexp_handling(pattern, name) - @regexp_handlings << [pattern, name] - end + raise + end + + ## + # Take a block of text and use various heuristics to determine its + # structure (paragraphs, lists, and so on). Invoke an event handler as we + # identify significant chunks. + + def initialize + @regexp_handlings = [] + @output = nil + end + + ## + # Add to other inline sequences. For example, we could add WikiWords using + # something like: + # + # parser.add_regexp_handling(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) + # + # Each wiki word will be presented to the output formatter. + + def add_regexp_handling(pattern, name) + @regexp_handlings << [pattern, name] + end + + ## + # We take +input+, parse it if necessary, then invoke the output +formatter+ + # using a Visitor to render the result. + + def convert(input, formatter) + document = case input + when Markup::Document + input + else + Markup::Parser.parse input + end + + document.accept formatter + end + + autoload :Parser, "#{__dir__}/markup/parser" + autoload :InlineParser, "#{__dir__}/markup/inline_parser" + autoload :PreProcess, "#{__dir__}/markup/pre_process" + + # RDoc::Markup AST + autoload :BlankLine, "#{__dir__}/markup/blank_line" + autoload :BlockQuote, "#{__dir__}/markup/block_quote" + autoload :Document, "#{__dir__}/markup/document" + autoload :Element, "#{__dir__}/markup/element" + autoload :HardBreak, "#{__dir__}/markup/hard_break" + autoload :Heading, "#{__dir__}/markup/heading" + autoload :Include, "#{__dir__}/markup/include" + autoload :IndentedParagraph, "#{__dir__}/markup/indented_paragraph" + autoload :List, "#{__dir__}/markup/list" + autoload :ListItem, "#{__dir__}/markup/list_item" + autoload :Paragraph, "#{__dir__}/markup/paragraph" + autoload :Table, "#{__dir__}/markup/table" + autoload :Raw, "#{__dir__}/markup/raw" + autoload :Rule, "#{__dir__}/markup/rule" + autoload :Verbatim, "#{__dir__}/markup/verbatim" + + # Formatters + autoload :Formatter, "#{__dir__}/markup/formatter" + + autoload :ToAnsi, "#{__dir__}/markup/to_ansi" + autoload :ToBs, "#{__dir__}/markup/to_bs" + autoload :ToHtml, "#{__dir__}/markup/to_html" + autoload :ToHtmlCrossref, "#{__dir__}/markup/to_html_crossref" + autoload :ToHtmlSnippet, "#{__dir__}/markup/to_html_snippet" + autoload :ToLabel, "#{__dir__}/markup/to_label" + autoload :ToMarkdown, "#{__dir__}/markup/to_markdown" + autoload :ToRdoc, "#{__dir__}/markup/to_rdoc" + autoload :ToTableOfContents, "#{__dir__}/markup/to_table_of_contents" + autoload :ToTest, "#{__dir__}/markup/to_test" + autoload :ToTtOnly, "#{__dir__}/markup/to_tt_only" - ## - # We take +input+, parse it if necessary, then invoke the output +formatter+ - # using a Visitor to render the result. - - def convert(input, formatter) - document = case input - when RDoc::Markup::Document - input - else - RDoc::Markup::Parser.parse input - end - - document.accept formatter end - - autoload :Parser, "#{__dir__}/markup/parser" - autoload :InlineParser, "#{__dir__}/markup/inline_parser" - autoload :PreProcess, "#{__dir__}/markup/pre_process" - - # RDoc::Markup AST - autoload :BlankLine, "#{__dir__}/markup/blank_line" - autoload :BlockQuote, "#{__dir__}/markup/block_quote" - autoload :Document, "#{__dir__}/markup/document" - autoload :Element, "#{__dir__}/markup/element" - autoload :HardBreak, "#{__dir__}/markup/hard_break" - autoload :Heading, "#{__dir__}/markup/heading" - autoload :Include, "#{__dir__}/markup/include" - autoload :IndentedParagraph, "#{__dir__}/markup/indented_paragraph" - autoload :List, "#{__dir__}/markup/list" - autoload :ListItem, "#{__dir__}/markup/list_item" - autoload :Paragraph, "#{__dir__}/markup/paragraph" - autoload :Table, "#{__dir__}/markup/table" - autoload :Raw, "#{__dir__}/markup/raw" - autoload :Rule, "#{__dir__}/markup/rule" - autoload :Verbatim, "#{__dir__}/markup/verbatim" - - # Formatters - autoload :Formatter, "#{__dir__}/markup/formatter" - - autoload :ToAnsi, "#{__dir__}/markup/to_ansi" - autoload :ToBs, "#{__dir__}/markup/to_bs" - autoload :ToHtml, "#{__dir__}/markup/to_html" - autoload :ToHtmlCrossref, "#{__dir__}/markup/to_html_crossref" - autoload :ToHtmlSnippet, "#{__dir__}/markup/to_html_snippet" - autoload :ToLabel, "#{__dir__}/markup/to_label" - autoload :ToMarkdown, "#{__dir__}/markup/to_markdown" - autoload :ToRdoc, "#{__dir__}/markup/to_rdoc" - autoload :ToTableOfContents, "#{__dir__}/markup/to_table_of_contents" - autoload :ToTest, "#{__dir__}/markup/to_test" - autoload :ToTtOnly, "#{__dir__}/markup/to_tt_only" - end diff --git a/lib/rdoc/markup/block_quote.rb b/lib/rdoc/markup/block_quote.rb index d2f13220f6..79b7d37cb6 100644 --- a/lib/rdoc/markup/block_quote.rb +++ b/lib/rdoc/markup/block_quote.rb @@ -1,14 +1,18 @@ # frozen_string_literal: true -## -# A quoted section which contains markup items. +module RDoc + class Markup + ## + # A quoted section which contains markup items. -class RDoc::Markup::BlockQuote < RDoc::Markup::Raw + class BlockQuote < Markup::Raw - ## - # Calls #accept_block_quote on +visitor+ + ## + # Calls #accept_block_quote on +visitor+ - def accept(visitor) - visitor.accept_block_quote self - end + def accept(visitor) + visitor.accept_block_quote self + end + end + end end diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb index 20f431bdf0..7d0f8ea396 100644 --- a/lib/rdoc/markup/document.rb +++ b/lib/rdoc/markup/document.rb @@ -1,164 +1,168 @@ # frozen_string_literal: true -## -# A Document containing lists, headings, paragraphs, etc. +module RDoc + class Markup + ## + # A Document containing lists, headings, paragraphs, etc. -class RDoc::Markup::Document + class Document - include Enumerable + include Enumerable - ## - # The file this document was created from. See also - # RDoc::ClassModule#add_comment + ## + # The file this document was created from. See also + # RDoc::ClassModule#add_comment - attr_reader :file + attr_reader :file - ## - # If a heading is below the given level it will be omitted from the - # table_of_contents + ## + # If a heading is below the given level it will be omitted from the + # table_of_contents - attr_accessor :omit_headings_below + attr_accessor :omit_headings_below - ## - # The parts of the Document + ## + # The parts of the Document - attr_reader :parts + attr_reader :parts - ## - # Creates a new Document with +parts+ + ## + # Creates a new Document with +parts+ - def initialize(*parts) - @parts = [] - @parts.concat parts + def initialize(*parts) + @parts = [] + @parts.concat parts - @file = nil - @omit_headings_from_table_of_contents_below = nil - end - - ## - # Appends +part+ to the document + @file = nil + @omit_headings_from_table_of_contents_below = nil + end - def <<(part) - case part - when RDoc::Markup::Document - unless part.empty? - parts.concat part.parts - parts << RDoc::Markup::BlankLine.new + ## + # Appends +part+ to the document + + def <<(part) + case part + when Markup::Document + unless part.empty? + parts.concat part.parts + parts << Markup::BlankLine.new + end + when String + raise ArgumentError, + "expected RDoc::Markup::Document and friends, got String" unless + part.empty? + else + parts << part + end end - when String - raise ArgumentError, - "expected RDoc::Markup::Document and friends, got String" unless - part.empty? - else - parts << part - end - end - def ==(other) # :nodoc: - self.class == other.class and - @file == other.file and - @parts == other.parts - end + def ==(other) # :nodoc: + self.class == other.class and + @file == other.file and + @parts == other.parts + end - ## - # Runs this document and all its #items through +visitor+ + ## + # Runs this document and all its #items through +visitor+ - def accept(visitor) - visitor.start_accepting + def accept(visitor) + visitor.start_accepting - visitor.accept_document self + visitor.accept_document self - visitor.end_accepting - end + visitor.end_accepting + end - ## - # Concatenates the given +parts+ onto the document + ## + # Concatenates the given +parts+ onto the document - def concat(parts) - self.parts.concat parts - end + def concat(parts) + self.parts.concat parts + end - ## - # Enumerator for the parts of this document + ## + # Enumerator for the parts of this document - def each(&block) - @parts.each(&block) - end + def each(&block) + @parts.each(&block) + end - ## - # Does this document have no parts? + ## + # Does this document have no parts? - def empty? - @parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?) - end + def empty? + @parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?) + end - ## - # The file this Document was created from. + ## + # The file this Document was created from. - def file=(location) - @file = case location - when RDoc::TopLevel - location.relative_name - else - location - end - end - - ## - # When this is a collection of documents (#file is not set and this document - # contains only other documents as its direct children) #merge replaces - # documents in this class with documents from +other+ when the file matches - # and adds documents from +other+ when the files do not. - # - # The information in +other+ is preferred over the receiver - - def merge(other) - if empty? - @parts = other.parts - return self - end + def file=(location) + @file = case location + when TopLevel + location.relative_name + else + location + end + end - other.parts.each do |other_part| - self.parts.delete_if do |self_part| - self_part.file and self_part.file == other_part.file + ## + # When this is a collection of documents (#file is not set and this document + # contains only other documents as its direct children) #merge replaces + # documents in this class with documents from +other+ when the file matches + # and adds documents from +other+ when the files do not. + # + # The information in +other+ is preferred over the receiver + + def merge(other) + if empty? + @parts = other.parts + return self + end + + other.parts.each do |other_part| + self.parts.delete_if do |self_part| + self_part.file and self_part.file == other_part.file + end + + self.parts << other_part + end + + self end - self.parts << other_part - end + ## + # Does this Document contain other Documents? - self - end + def merged? + Markup::Document === @parts.first + end - ## - # Does this Document contain other Documents? + def pretty_print(q) # :nodoc: + start = @file ? "[doc (#{@file}): " : '[doc: ' - def merged? - RDoc::Markup::Document === @parts.first - end + q.group 2, start, ']' do + q.seplist @parts do |part| + q.pp part + end + end + end - def pretty_print(q) # :nodoc: - start = @file ? "[doc (#{@file}): " : '[doc: ' + ## + # Appends +parts+ to the document - q.group 2, start, ']' do - q.seplist @parts do |part| - q.pp part + def push(*parts) + self.parts.concat parts end - end - end - ## - # Appends +parts+ to the document - - def push(*parts) - self.parts.concat parts - end + ## + # Returns an Array of headings in the document. + # + # Require 'rdoc/markup/formatter' before calling this method. - ## - # Returns an Array of headings in the document. - # - # Require 'rdoc/markup/formatter' before calling this method. + def table_of_contents + accept Markup::ToTableOfContents.to_toc + end - def table_of_contents - accept RDoc::Markup::ToTableOfContents.to_toc + end end - end diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb index 58b8ba9755..b2e7435ecb 100644 --- a/lib/rdoc/markup/formatter.rb +++ b/lib/rdoc/markup/formatter.rb @@ -12,274 +12,278 @@ require 'rdoc/markup/inline_parser' -class RDoc::Markup::Formatter +module RDoc + class Markup + class Formatter - ## - # Converts a target url to one that is relative to a given path + ## + # Converts a target url to one that is relative to a given path - def self.gen_relative_url(path, target) - from = File.dirname path - to, to_file = File.split target + def self.gen_relative_url(path, target) + from = File.dirname path + to, to_file = File.split target - from = from.split "/" - to = to.split "/" + from = from.split "/" + to = to.split "/" - from.delete '.' - to.delete '.' + from.delete '.' + to.delete '.' - while from.size > 0 and to.size > 0 and from[0] == to[0] do - from.shift - to.shift - end - - from.fill ".." - from.concat to - from << to_file - File.join(*from) - end - - ## - # Creates a new Formatter + while from.size > 0 and to.size > 0 and from[0] == to[0] do + from.shift + to.shift + end - def initialize - @markup = RDoc::Markup.new + from.fill ".." + from.concat to + from << to_file + File.join(*from) + end - @from_path = '.' - end + ## + # Creates a new Formatter - ## - # Adds +document+ to the output + def initialize + @markup = Markup.new - def accept_document(document) - document.parts.each do |item| - case item - when RDoc::Markup::Document # HACK - accept_document item - else - item.accept self + @from_path = '.' end - end - end - ## - # Adds a regexp handling for links of the form rdoc-...: + ## + # Adds +document+ to the output + + def accept_document(document) + document.parts.each do |item| + case item + when Markup::Document # HACK + accept_document item + else + item.accept self + end + end + end - def add_regexp_handling_RDOCLINK - @markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK) - end + ## + # Adds a regexp handling for links of the form rdoc-...: - ## - # Allows +tag+ to be decorated with additional information. + def add_regexp_handling_RDOCLINK + @markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK) + end - def annotate(tag) - tag - end + ## + # Allows +tag+ to be decorated with additional information. - ## - # Marks up +content+ + def annotate(tag) + tag + end - def convert(content) - @markup.convert content, self - end + ## + # Marks up +content+ - # Applies regexp handling to +text+ and returns an array of [text, converted?] pairs. + def convert(content) + @markup.convert content, self + end - def apply_regexp_handling(text) - matched = [] - @markup.regexp_handlings.each_with_index do |(pattern, name), priority| - text.scan(pattern) do - m = Regexp.last_match - idx = m[1] ? 1 : 0 - matched << [m.begin(idx), m.end(idx), m[idx], name, priority] + # Applies regexp handling to +text+ and returns an array of [text, converted?] pairs. + + def apply_regexp_handling(text) + matched = [] + @markup.regexp_handlings.each_with_index do |(pattern, name), priority| + text.scan(pattern) do + m = Regexp.last_match + idx = m[1] ? 1 : 0 + matched << [m.begin(idx), m.end(idx), m[idx], name, priority] + end + end + # If the start positions are the same, prefer the earlier-registered one + # (lower numeric priority from each_with_index). + matched.sort_by! {|beg_pos, _, _, _, priority| [beg_pos, priority] } + + pos = 0 + output = [] + matched.each do |beg_pos, end_pos, s, name| + next if beg_pos < pos + + output << [text[pos...beg_pos], false] if beg_pos != pos + handled = public_send(:"handle_regexp_#{name}", s) + output << [handled, true] + pos = end_pos + end + + output << [text[pos..], false] if pos < text.size + output end - end - # If the start positions are the same, prefer the earlier-registered one - # (lower numeric priority from each_with_index). - matched.sort_by! {|beg_pos, _, _, _, priority| [beg_pos, priority] } - - pos = 0 - output = [] - matched.each do |beg_pos, end_pos, s, name| - next if beg_pos < pos - - output << [text[pos...beg_pos], false] if beg_pos != pos - handled = public_send(:"handle_regexp_#{name}", s) - output << [handled, true] - pos = end_pos - end - output << [text[pos..], false] if pos < text.size - output - end + # Called when processing plain text while traversing inline nodes from handle_inline. + # +text+ may need proper escaping. - # Called when processing plain text while traversing inline nodes from handle_inline. - # +text+ may need proper escaping. + def handle_PLAIN_TEXT(text) + end - def handle_PLAIN_TEXT(text) - end + # Called when processing regexp-handling-processed text while traversing inline nodes from handle_inline. + # +text+ may contain markup tags. - # Called when processing regexp-handling-processed text while traversing inline nodes from handle_inline. - # +text+ may contain markup tags. + def handle_REGEXP_HANDLING_TEXT(text) + end - def handle_REGEXP_HANDLING_TEXT(text) - end + # Called when processing text node while traversing inline nodes from handle_inline. + # Apply regexp handling and dispatch to the appropriate handler: handle_REGEXP_HANDLING_TEXT or handle_PLAIN_TEXT. + + def handle_TEXT(text) + apply_regexp_handling(text).each do |part, converted| + if converted + handle_REGEXP_HANDLING_TEXT(part) + else + handle_PLAIN_TEXT(part) + end + end + end - # Called when processing text node while traversing inline nodes from handle_inline. - # Apply regexp handling and dispatch to the appropriate handler: handle_REGEXP_HANDLING_TEXT or handle_PLAIN_TEXT. + # Called when processing a hard break while traversing inline nodes from handle_inline. - def handle_TEXT(text) - apply_regexp_handling(text).each do |part, converted| - if converted - handle_REGEXP_HANDLING_TEXT(part) - else - handle_PLAIN_TEXT(part) + def handle_HARD_BREAK end - end - end - # Called when processing a hard break while traversing inline nodes from handle_inline. + # Called when processing bold nodes while traversing inline nodes from handle_inline. + # Traverse the children nodes and dispatch to the appropriate handlers. - def handle_HARD_BREAK - end + def handle_BOLD(nodes) + traverse_inline_nodes(nodes) + end - # Called when processing bold nodes while traversing inline nodes from handle_inline. - # Traverse the children nodes and dispatch to the appropriate handlers. + # Called when processing emphasis nodes while traversing inline nodes from handle_inline. + # Traverse the children nodes and dispatch to the appropriate handlers. - def handle_BOLD(nodes) - traverse_inline_nodes(nodes) - end + def handle_EM(nodes) + traverse_inline_nodes(nodes) + end - # Called when processing emphasis nodes while traversing inline nodes from handle_inline. - # Traverse the children nodes and dispatch to the appropriate handlers. + # Called when processing bold word nodes while traversing inline nodes from handle_inline. + # +word+ may need proper escaping. - def handle_EM(nodes) - traverse_inline_nodes(nodes) - end + def handle_BOLD_WORD(word) + handle_PLAIN_TEXT(word) + end - # Called when processing bold word nodes while traversing inline nodes from handle_inline. - # +word+ may need proper escaping. + # Called when processing emphasis word nodes while traversing inline nodes from handle_inline. + # +word+ may need proper escaping. - def handle_BOLD_WORD(word) - handle_PLAIN_TEXT(word) - end + def handle_EM_WORD(word) + handle_PLAIN_TEXT(word) + end - # Called when processing emphasis word nodes while traversing inline nodes from handle_inline. - # +word+ may need proper escaping. + # Called when processing tt nodes while traversing inline nodes from handle_inline. + # +code+ may need proper escaping. - def handle_EM_WORD(word) - handle_PLAIN_TEXT(word) - end + def handle_TT(code) + handle_PLAIN_TEXT(code) + end - # Called when processing tt nodes while traversing inline nodes from handle_inline. - # +code+ may need proper escaping. + # Called when processing strike nodes while traversing inline nodes from handle_inline. + # Traverse the children nodes and dispatch to the appropriate handlers. - def handle_TT(code) - handle_PLAIN_TEXT(code) - end + def handle_STRIKE(nodes) + traverse_inline_nodes(nodes) + end - # Called when processing strike nodes while traversing inline nodes from handle_inline. - # Traverse the children nodes and dispatch to the appropriate handlers. + # Called when processing tidylink nodes while traversing inline nodes from handle_inline. + # +label_part+ is an array of strings or nodes representing the link label. + # +url+ is the link URL. + # Traverse the label_part nodes and dispatch to the appropriate handlers. - def handle_STRIKE(nodes) - traverse_inline_nodes(nodes) - end + def handle_TIDYLINK(label_part, url) + traverse_inline_nodes(label_part) + end - # Called when processing tidylink nodes while traversing inline nodes from handle_inline. - # +label_part+ is an array of strings or nodes representing the link label. - # +url+ is the link URL. - # Traverse the label_part nodes and dispatch to the appropriate handlers. + # Parses inline +text+, traverse the resulting nodes, and calls the appropriate handler methods. - def handle_TIDYLINK(label_part, url) - traverse_inline_nodes(label_part) - end + def handle_inline(text) + nodes = Markup::InlineParser.new(text).parse + traverse_inline_nodes(nodes) + end - # Parses inline +text+, traverse the resulting nodes, and calls the appropriate handler methods. + # Traverses +nodes+ and calls the appropriate handler methods + # Nodes formats are described in RDoc::Markup::InlineParser#parse + + def traverse_inline_nodes(nodes) + nodes.each do |node| + next handle_TEXT(node) if String === node + case node[:type] + when :TIDYLINK + handle_TIDYLINK(node[:children], node[:url]) + when :HARD_BREAK + handle_HARD_BREAK + when :BOLD + handle_BOLD(node[:children]) + when :BOLD_WORD + handle_BOLD_WORD(node[:children][0] || '') + when :EM + handle_EM(node[:children]) + when :EM_WORD + handle_EM_WORD(node[:children][0] || '') + when :TT + handle_TT(node[:children][0] || '') + when :STRIKE + handle_STRIKE(node[:children]) + end + end + end - def handle_inline(text) - nodes = RDoc::Markup::InlineParser.new(text).parse - traverse_inline_nodes(nodes) - end + ## + # Converts a string to be fancier if desired - # Traverses +nodes+ and calls the appropriate handler methods - # Nodes formats are described in RDoc::Markup::InlineParser#parse - - def traverse_inline_nodes(nodes) - nodes.each do |node| - next handle_TEXT(node) if String === node - case node[:type] - when :TIDYLINK - handle_TIDYLINK(node[:children], node[:url]) - when :HARD_BREAK - handle_HARD_BREAK - when :BOLD - handle_BOLD(node[:children]) - when :BOLD_WORD - handle_BOLD_WORD(node[:children][0] || '') - when :EM - handle_EM(node[:children]) - when :EM_WORD - handle_EM_WORD(node[:children][0] || '') - when :TT - handle_TT(node[:children][0] || '') - when :STRIKE - handle_STRIKE(node[:children]) + def convert_string(string) + string end - end - end - ## - # Converts a string to be fancier if desired + ## + # Use ignore in your subclass to ignore the content of a node. + # + # ## + # # We don't support raw nodes in ToNoRaw + # + # alias accept_raw ignore - def convert_string(string) - string - end + def ignore(*node) + end - ## - # Use ignore in your subclass to ignore the content of a node. - # - # ## - # # We don't support raw nodes in ToNoRaw - # - # alias accept_raw ignore + ## + # Extracts and a scheme, url and an anchor id from +url+ and returns them. + + def parse_url(url) + case url + when /^rdoc-label:([^:]*)(?::(.*))?/ + scheme = 'link' + path = "##{$1}" + id = " id=\"#{$2}\"" if $2 + when /([A-Za-z]+):(.*)/ + scheme = $1.downcase + path = $2 + when /^#/ + else + scheme = 'http' + path = url + url = url + end + + if scheme == 'link' + url = if path[0, 1] == '#' # is this meaningful? + path + else + self.class.gen_relative_url @from_path, path + end + end + + [scheme, url, id] + end - def ignore(*node) - end + ## + # Is +tag+ a tt tag? - ## - # Extracts and a scheme, url and an anchor id from +url+ and returns them. - - def parse_url(url) - case url - when /^rdoc-label:([^:]*)(?::(.*))?/ - scheme = 'link' - path = "##{$1}" - id = " id=\"#{$2}\"" if $2 - when /([A-Za-z]+):(.*)/ - scheme = $1.downcase - path = $2 - when /^#/ - else - scheme = 'http' - path = url - url = url - end + def tt?(tag) + tag.bit == @tt_bit + end - if scheme == 'link' - url = if path[0, 1] == '#' # is this meaningful? - path - else - self.class.gen_relative_url @from_path, path - end end - - [scheme, url, id] end - - ## - # Is +tag+ a tt tag? - - def tt?(tag) - tag.bit == @tt_bit - end - end diff --git a/lib/rdoc/markup/include.rb b/lib/rdoc/markup/include.rb index 56775b25b6..345c5fbfa3 100644 --- a/lib/rdoc/markup/include.rb +++ b/lib/rdoc/markup/include.rb @@ -1,42 +1,46 @@ # frozen_string_literal: true -## -# A file included at generation time. Objects of this class are created by -# RDoc::RD for an extension-less include. -# -# This implementation in incomplete. +module RDoc + class Markup + ## + # A file included at generation time. Objects of this class are created by + # RDoc::RD for an extension-less include. + # + # This implementation in incomplete. -class RDoc::Markup::Include + class Include - ## - # The filename to be included, without extension + ## + # The filename to be included, without extension - attr_reader :file + attr_reader :file - ## - # Directories to search for #file + ## + # Directories to search for #file - attr_reader :include_path + attr_reader :include_path - ## - # Creates a new include that will import +file+ from +include_path+ + ## + # Creates a new include that will import +file+ from +include_path+ - def initialize(file, include_path) - @file = file - @include_path = include_path - end + def initialize(file, include_path) + @file = file + @include_path = include_path + end - def ==(other) # :nodoc: - self.class === other and - @file == other.file and @include_path == other.include_path - end + def ==(other) # :nodoc: + self.class === other and + @file == other.file and @include_path == other.include_path + end + + def pretty_print(q) # :nodoc: + q.group 2, '[incl ', ']' do + q.text file + q.breakable + q.text 'from ' + q.pp include_path + end + end - def pretty_print(q) # :nodoc: - q.group 2, '[incl ', ']' do - q.text file - q.breakable - q.text 'from ' - q.pp include_path end end - end diff --git a/lib/rdoc/markup/indented_paragraph.rb b/lib/rdoc/markup/indented_paragraph.rb index c28f6f605f..6b923fed50 100644 --- a/lib/rdoc/markup/indented_paragraph.rb +++ b/lib/rdoc/markup/indented_paragraph.rb @@ -1,47 +1,51 @@ # frozen_string_literal: true -## -# An Indented Paragraph of text +module RDoc + class Markup + ## + # An Indented Paragraph of text -class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw + class IndentedParagraph < Markup::Raw - ## - # The indent in number of spaces + ## + # The indent in number of spaces - attr_reader :indent + attr_reader :indent - ## - # Creates a new IndentedParagraph containing +parts+ indented with +indent+ - # spaces + ## + # Creates a new IndentedParagraph containing +parts+ indented with +indent+ + # spaces - def initialize(indent, *parts) - @indent = indent + def initialize(indent, *parts) + @indent = indent - super(*parts) - end + super(*parts) + end - def ==(other) # :nodoc: - super and indent == other.indent - end + def ==(other) # :nodoc: + super and indent == other.indent + end - ## - # Calls #accept_indented_paragraph on +visitor+ + ## + # Calls #accept_indented_paragraph on +visitor+ - def accept(visitor) - visitor.accept_indented_paragraph self - end - - ## - # Joins the raw paragraph text and converts inline HardBreaks to the - # +hard_break+ text followed by the indent. + def accept(visitor) + visitor.accept_indented_paragraph self + end - def text(hard_break = nil) - @parts.map do |part| - if RDoc::Markup::HardBreak === part - '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break - else - part + ## + # Joins the raw paragraph text and converts inline HardBreaks to the + # +hard_break+ text followed by the indent. + + def text(hard_break = nil) + @parts.map do |part| + if Markup::HardBreak === part + '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break + else + part + end + end.join end - end.join - end + end + end end diff --git a/lib/rdoc/markup/inline_parser.rb b/lib/rdoc/markup/inline_parser.rb index 4e2b86c630..a5eaba4cb4 100644 --- a/lib/rdoc/markup/inline_parser.rb +++ b/lib/rdoc/markup/inline_parser.rb @@ -3,310 +3,314 @@ require 'set' require 'strscan' -# Parses inline markup in RDoc text. -# This parser handles em, bold, strike, tt, hard break, and tidylink. -# Block-level constructs are handled in RDoc::Markup::Parser. - -class RDoc::Markup::InlineParser - - # TT, BOLD_WORD, EM_WORD: regexp-handling(example: crossref) is disabled - WORD_PAIRS = { - '*' => :BOLD_WORD, - '**' => :BOLD_WORD, - '_' => :EM_WORD, - '__' => :EM_WORD, - '+' => :TT, - '++' => :TT, - '`' => :TT, - '``' => :TT - } # :nodoc: - - # Other types: regexp-handling(example: crossref) is enabled - TAGS = { - 'em' => :EM, - 'i' => :EM, - 'b' => :BOLD, - 's' => :STRIKE, - 'del' => :STRIKE, - } # :nodoc: - - STANDALONE_TAGS = { 'br' => :HARD_BREAK } # :nodoc: - - CODEBLOCK_TAGS = %w[tt code] # :nodoc: - - TOKENS = { - **WORD_PAIRS.transform_values { [:word_pair, nil] }, - **TAGS.keys.to_h {|tag| ["<#{tag}>", [:open_tag, tag]] }, - **TAGS.keys.to_h {|tag| ["#{tag}>", [:close_tag, tag]] }, - **CODEBLOCK_TAGS.to_h {|tag| ["<#{tag}>", [:code_start, tag]] }, - **STANDALONE_TAGS.keys.to_h {|tag| ["<#{tag}>", [:standalone_tag, tag]] }, - '{' => [:tidylink_start, nil], - '}' => [:tidylink_mid, nil], - '\\' => [:escape, nil], - '[' => nil # To make `label[url]` scan as separate tokens - } # :nodoc: - - multi_char_tokens_regexp = Regexp.union(TOKENS.keys.select {|s| s.size > 1 }).source - token_starts_regexp = TOKENS.keys.map {|s| s[0] }.uniq.map {|s| Regexp.escape(s) }.join - - SCANNER_REGEXP = - /(?: - #{multi_char_tokens_regexp} - |[^#{token_starts_regexp}\sa-zA-Z0-9\.]+ # chunk of normal text - |\s+|[a-zA-Z0-9\.]+|. - )/x # :nodoc: - - # Characters that can be escaped with backslash. - ESCAPING_CHARS = '\\*_+`{}[]<>' # :nodoc: - - # Pattern to match code block content until
or .
+ CODEBLOCK_REGEXPS = CODEBLOCK_TAGS.to_h {|name| [name, /((?:\\.|[^\\])*?)<\/#{name}>/] } # :nodoc:
+
+ # Word contains alphanumeric and _./:[]- characters.
+ # Word may start with # and may end with any non-space character. (e.g. #eql?).
+ # Underscore delimiter have special rules.
+ WORD_REGEXPS = {
+ # Words including _, longest match.
+ # Example: `_::A_` `_-42_` `_A::B::C.foo_bar[baz]_` `_kwarg:_`
+ # Content must not include _ followed by non-alphanumeric character
+ # Example: `_host_:_port_` will be `_host_` + `:` + `_port_`
+ '_' => /#?([a-zA-Z0-9.\/:\[\]-]|_+[a-zA-Z0-9])+[^\s]?_(?=[^a-zA-Z0-9_]|\z)/,
+ # Words allowing _ but not allowing __
+ '__' => /#?[a-zA-Z0-9.\/:\[\]-]*(_[a-zA-Z0-9.\/:\[\]-]+)*[^\s]?__(?=[^a-zA-Z0-9]|\z)/,
+ **%w[* ** + ++ ` ``].to_h do |s|
+ # normal words that can be used within +word+ or *word*
+ [s, /#?[a-zA-Z0-9_.\/:\[\]-]+[^\s]?#{Regexp.escape(s)}(?=[^a-zA-Z0-9]|\z)/]
end
+ } # :nodoc:
+
+ def initialize(string)
+ @scanner = StringScanner.new(string)
+ @last_match = nil
+ @scanner_negative_cache = Set.new
+ @stack = []
+ @delimiters = {}
end
- next unless close
+ # Return the current parsing node on @stack.
- while current[:delimiter] != close
- children = current[:children]
- open_token = current[:token]
- stack_pop
- current[:children] << open_token if open_token
- current[:children].concat(children)
+ def current
+ @stack.last
end
- token = current[:token]
- children = compact_string(current[:children])
- stack_pop
-
- return children if close == :root
-
- if close == :tidylink || close == :invalidated_tidylink
- if tidylink_url
- current[:children] << { type: :TIDYLINK, children: children, url: tidylink_url }
- invalidate_open_tidylinks
- else
- current[:children] << token
- current[:children].concat(children)
+ # Parse and return an array of nodes.
+ # Node format:
+ # {
+ # type: :EM | :BOLD | :BOLD_WORD | :EM_WORD | :TT | :STRIKE | :HARD_BREAK | :TIDYLINK,
+ # url: string # only for :TIDYLINK
+ # children: [string_or_node, ...]
+ # }
+
+ def parse
+ stack_push(:root, nil)
+ while true
+ type, token, value = scan_token
+ close = nil
+ tidylink_url = nil
+ case type
+ when :node
+ current[:children] << value
+ invalidate_open_tidylinks if value[:type] == :TIDYLINK
+ when :eof
+ close = :root
+ when :tidylink_open
+ stack_push(:tidylink, token)
+ when :tidylink_close
+ close = :tidylink
+ if value
+ tidylink_url = value
+ else
+ # Tidylink closing brace without URL part. Treat opening and closing braces as normal text
+ # `{labelnodes}...` case.
+ current[:children] << token
+ end
+ when :invalidated_tidylink_close
+ # `{...{label}[url]...}` case. Nested tidylink invalidates outer one. The last `}` closes the invalidated tidylink.
+ current[:children] << token
+ close = :invalidated_tidylink
+ when :text
+ current[:children] << token
+ when :open
+ stack_push(value, token)
+ when :close
+ if @delimiters[value]
+ close = value
+ else
+ # closing tag without matching opening tag. Treat as normal text.
+ current[:children] << token
+ end
+ end
+
+ next unless close
+
+ while current[:delimiter] != close
+ children = current[:children]
+ open_token = current[:token]
+ stack_pop
+ current[:children] << open_token if open_token
+ current[:children].concat(children)
+ end
+
+ token = current[:token]
+ children = compact_string(current[:children])
+ stack_pop
+
+ return children if close == :root
+
+ if close == :tidylink || close == :invalidated_tidylink
+ if tidylink_url
+ current[:children] << { type: :TIDYLINK, children: children, url: tidylink_url }
+ invalidate_open_tidylinks
+ else
+ current[:children] << token
+ current[:children].concat(children)
+ end
+ else
+ current[:children] << { type: TAGS[close], children: children }
+ end
end
- else
- current[:children] << { type: TAGS[close], children: children }
end
- end
- end
private
- # When a valid tidylink node is encountered, invalidate all nested tidylinks.
+ # When a valid tidylink node is encountered, invalidate all nested tidylinks.
- def invalidate_open_tidylinks
- return unless @delimiters[:tidylink]
+ def invalidate_open_tidylinks
+ return unless @delimiters[:tidylink]
- @delimiters[:invalidated_tidylink] ||= []
- @delimiters[:tidylink].each do |idx|
- @delimiters[:invalidated_tidylink] << idx
- @stack[idx][:delimiter] = :invalidated_tidylink
- end
- @delimiters.delete(:tidylink)
- end
-
- # Pop the top node off the stack when node is closed by a closing delimiter or an error.
+ @delimiters[:invalidated_tidylink] ||= []
+ @delimiters[:tidylink].each do |idx|
+ @delimiters[:invalidated_tidylink] << idx
+ @stack[idx][:delimiter] = :invalidated_tidylink
+ end
+ @delimiters.delete(:tidylink)
+ end
- def stack_pop
- delimiter = current[:delimiter]
- @delimiters[delimiter].pop
- @delimiters.delete(delimiter) if @delimiters[delimiter].empty?
- @stack.pop
- end
+ # Pop the top node off the stack when node is closed by a closing delimiter or an error.
- # Push a new node onto the stack when encountering an opening delimiter.
+ def stack_pop
+ delimiter = current[:delimiter]
+ @delimiters[delimiter].pop
+ @delimiters.delete(delimiter) if @delimiters[delimiter].empty?
+ @stack.pop
+ end
- def stack_push(delimiter, token)
- node = { delimiter: delimiter, token: token, children: [] }
- (@delimiters[delimiter] ||= []) << @stack.size
- @stack << node
- end
+ # Push a new node onto the stack when encountering an opening delimiter.
- # Compacts adjacent strings in +nodes+ into a single string.
+ def stack_push(delimiter, token)
+ node = { delimiter: delimiter, token: token, children: [] }
+ (@delimiters[delimiter] ||= []) << @stack.size
+ @stack << node
+ end
- def compact_string(nodes)
- nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
- is_str ? elems.join : elems
- end
- end
+ # Compacts adjacent strings in +nodes+ into a single string.
- # Scan from StringScanner with +pattern+
- # If +negative_cache+ is true, caches scan failure result. scan(pattern, negative_cache: true) return nil when it is called again after a failure.
- # Be careful to use +negative_cache+ with a pattern and position that does not match after previous failure.
+ def compact_string(nodes)
+ nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
+ is_str ? elems.join : elems
+ end
+ end
- def strscan(pattern, negative_cache: false)
- return if negative_cache && @scanner_negative_cache.include?(pattern)
+ # Scan from StringScanner with +pattern+
+ # If +negative_cache+ is true, caches scan failure result. scan(pattern, negative_cache: true) return nil when it is called again after a failure.
+ # Be careful to use +negative_cache+ with a pattern and position that does not match after previous failure.
- string = @scanner.scan(pattern)
- @last_match = string if string
- @scanner_negative_cache << pattern if !string && negative_cache
- string
- end
+ def strscan(pattern, negative_cache: false)
+ return if negative_cache && @scanner_negative_cache.include?(pattern)
- # Scan and return the next token for parsing.
- # Returns [token_type, token_string_or_nil, extra_info]
-
- def scan_token
- last_match = @last_match
- token = strscan(SCANNER_REGEXP)
- type, name = TOKENS[token]
-
- case type
- when :word_pair
- # If the character before word pair delimiter is alphanumeric, do not treat as word pair.
- word_pair = strscan(WORD_REGEXPS[token]) unless /[a-zA-Z0-9]\z/.match?(last_match)
-
- if word_pair.nil?
- [:text, token, nil]
- elsif token == '__' && word_pair.match?(/\A[a-zA-Z]+__\z/)
- # Special exception: __FILE__, __LINE__, __send__ should be treated as normal text.
- [:text, "#{token}#{word_pair}", nil]
- else
- [:node, nil, { type: WORD_PAIRS[token], children: [word_pair.delete_suffix(token)] }]
- end
- when :open_tag
- [:open, token, name]
- when :close_tag
- [:close, token, name]
- when :code_start
- if (codeblock = strscan(CODEBLOCK_REGEXPS[name], negative_cache: true))
- # Need to unescape `\\` and `\<`.
- # RDoc also unescapes backslash + word separators, but this is not really necessary.
- content = codeblock.delete_suffix("#{name}>").gsub(/\\(.)/) { '\\<*+_`'.include?($1) ? $1 : $& }
- [:node, nil, { type: :TT, children: content.empty? ? [] : [content] }]
- else
- [:text, token, nil]
+ string = @scanner.scan(pattern)
+ @last_match = string if string
+ @scanner_negative_cache << pattern if !string && negative_cache
+ string
end
- when :standalone_tag
- [:node, nil, { type: STANDALONE_TAGS[name], children: [] }]
- when :tidylink_start
- [:tidylink_open, token, nil]
- when :tidylink_mid
- if @delimiters[:tidylink]
- if (url = read_tidylink_url)
- [:tidylink_close, nil, url]
+
+ # Scan and return the next token for parsing.
+ # Returns [token_type, token_string_or_nil, extra_info]
+
+ def scan_token
+ last_match = @last_match
+ token = strscan(SCANNER_REGEXP)
+ type, name = TOKENS[token]
+
+ case type
+ when :word_pair
+ # If the character before word pair delimiter is alphanumeric, do not treat as word pair.
+ word_pair = strscan(WORD_REGEXPS[token]) unless /[a-zA-Z0-9]\z/.match?(last_match)
+
+ if word_pair.nil?
+ [:text, token, nil]
+ elsif token == '__' && word_pair.match?(/\A[a-zA-Z]+__\z/)
+ # Special exception: __FILE__, __LINE__, __send__ should be treated as normal text.
+ [:text, "#{token}#{word_pair}", nil]
+ else
+ [:node, nil, { type: WORD_PAIRS[token], children: [word_pair.delete_suffix(token)] }]
+ end
+ when :open_tag
+ [:open, token, name]
+ when :close_tag
+ [:close, token, name]
+ when :code_start
+ if (codeblock = strscan(CODEBLOCK_REGEXPS[name], negative_cache: true))
+ # Need to unescape `\\` and `\<`.
+ # RDoc also unescapes backslash + word separators, but this is not really necessary.
+ content = codeblock.delete_suffix("#{name}>").gsub(/\\(.)/) { '\\<*+_`'.include?($1) ? $1 : $& }
+ [:node, nil, { type: :TT, children: content.empty? ? [] : [content] }]
+ else
+ [:text, token, nil]
+ end
+ when :standalone_tag
+ [:node, nil, { type: STANDALONE_TAGS[name], children: [] }]
+ when :tidylink_start
+ [:tidylink_open, token, nil]
+ when :tidylink_mid
+ if @delimiters[:tidylink]
+ if (url = read_tidylink_url)
+ [:tidylink_close, nil, url]
+ else
+ [:tidylink_close, token, nil]
+ end
+ elsif @delimiters[:invalidated_tidylink]
+ [:invalidated_tidylink_close, token, nil]
+ else
+ [:text, token, nil]
+ end
+ when :escape
+ next_char = strscan(/./)
+ if next_char.nil?
+ # backslash at end of string
+ [:text, '\\', nil]
+ elsif next_char && ESCAPING_CHARS.include?(next_char)
+ # escaped character
+ [:text, next_char, nil]
+ else
+ # If next_char not an escaping character, it is treated as text token with backslash + next_char
+ # For example, backslash of `\Ruby` (suppressed crossref) remains.
+ [:text, "\\#{next_char}", nil]
+ end
else
- [:tidylink_close, token, nil]
+ if token.nil?
+ [:eof, nil, nil]
+ elsif token.match?(/\A[A-Za-z0-9]*\z/) && (url = read_tidylink_url)
+ # Simplified tidylink: label[url]
+ [:node, nil, { type: :TIDYLINK, children: [token], url: url }]
+ else
+ [:text, token, nil]
+ end
end
- elsif @delimiters[:invalidated_tidylink]
- [:invalidated_tidylink_close, token, nil]
- else
- [:text, token, nil]
- end
- when :escape
- next_char = strscan(/./)
- if next_char.nil?
- # backslash at end of string
- [:text, '\\', nil]
- elsif next_char && ESCAPING_CHARS.include?(next_char)
- # escaped character
- [:text, next_char, nil]
- else
- # If next_char not an escaping character, it is treated as text token with backslash + next_char
- # For example, backslash of `\Ruby` (suppressed crossref) remains.
- [:text, "\\#{next_char}", nil]
- end
- else
- if token.nil?
- [:eof, nil, nil]
- elsif token.match?(/\A[A-Za-z0-9]*\z/) && (url = read_tidylink_url)
- # Simplified tidylink: label[url]
- [:node, nil, { type: :TIDYLINK, children: [token], url: url }]
- else
- [:text, token, nil]
end
- end
- end
- # Read the URL part of a tidylink from the current position.
- # Returns nil if no valid URL part is found.
- # URL part is enclosed in square brackets and may contain escaped brackets.
- # Example: [http://example.com/?q=\[\]] represents http://example.com/?q=[].
- # If we're accepting rdoc-style links in markdown, url may include *+<_ with backslash escape.
+ # Read the URL part of a tidylink from the current position.
+ # Returns nil if no valid URL part is found.
+ # URL part is enclosed in square brackets and may contain escaped brackets.
+ # Example: [http://example.com/?q=\[\]] represents http://example.com/?q=[].
+ # If we're accepting rdoc-style links in markdown, url may include *+<_ with backslash escape.
- def read_tidylink_url
- bracketed_url = strscan(/\[([^\s\[\]\\]|\\[\[\]\\*+<_])+\]/)
- bracketed_url[1...-1].gsub(/\\(.)/, '\1') if bracketed_url
+ def read_tidylink_url
+ bracketed_url = strscan(/\[([^\s\[\]\\]|\\[\[\]\\*+<_])+\]/)
+ bracketed_url[1...-1].gsub(/\\(.)/, '\1') if bracketed_url
+ end
+ end
end
end
diff --git a/lib/rdoc/markup/paragraph.rb b/lib/rdoc/markup/paragraph.rb
index 2db7d8721c..ae8357290e 100644
--- a/lib/rdoc/markup/paragraph.rb
+++ b/lib/rdoc/markup/paragraph.rb
@@ -1,28 +1,32 @@
# frozen_string_literal: true
-##
-# A Paragraph of text
+module RDoc
+ class Markup
+ ##
+ # A Paragraph of text
-class RDoc::Markup::Paragraph < RDoc::Markup::Raw
+ class Paragraph < Markup::Raw
- ##
- # Calls #accept_paragraph on +visitor+
+ ##
+ # Calls #accept_paragraph on +visitor+
- def accept(visitor)
- visitor.accept_paragraph self
- end
+ def accept(visitor)
+ visitor.accept_paragraph self
+ end
- ##
- # Joins the raw paragraph text and converts inline HardBreaks to the
- # +hard_break+ text.
+ ##
+ # Joins the raw paragraph text and converts inline HardBreaks to the
+ # +hard_break+ text.
- def text(hard_break = '')
- @parts.map do |part|
- if RDoc::Markup::HardBreak === part
- hard_break
- else
- part
+ def text(hard_break = '')
+ @parts.map do |part|
+ if Markup::HardBreak === part
+ hard_break
+ else
+ part
+ end
+ end.join
end
- end.join
- end
+ end
+ end
end
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 9b2e0aaa74..9e26611eb5 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -1,585 +1,589 @@
# frozen_string_literal: true
require 'strscan'
-##
-# A recursive-descent parser for RDoc markup.
-#
-# The parser tokenizes an input string then parses the tokens into a Document.
-# Documents can be converted into output formats by writing a visitor like
-# RDoc::Markup::ToHTML.
-#
-# The parser only handles the block-level constructs Paragraph, List,
-# ListItem, Heading, Verbatim, BlankLine, Rule and BlockQuote.
-# Inline markup such as \+blah\+ is handled separately by
-# RDoc::Markup::InlineParser.
-#
-# To see what markup the Parser implements read RDoc. To see how to use
-# RDoc markup to format text in your program read RDoc::Markup.
-
-class RDoc::Markup::Parser
-
- include RDoc::Text
-
- ##
- # List token types
-
- LIST_TOKENS = [
- :BULLET,
- :LABEL,
- :LALPHA,
- :NOTE,
- :NUMBER,
- :UALPHA,
- ]
-
- ##
- # Parser error subclass
-
- class Error < RuntimeError; end
-
- ##
- # Raised when the parser is unable to handle the given markup
-
- class ParseError < Error; end
-
- ##
- # Enables display of debugging information
-
- attr_accessor :debug
-
- ##
- # Token accessor
-
- attr_reader :tokens
-
- ##
- # Parses +str+ into a Document.
- #
- # Use RDoc::Markup#parse instead of this method.
-
- def self.parse(str)
- parser = new
- parser.tokenize str
- doc = RDoc::Markup::Document.new
- parser.parse doc
- end
-
- ##
- # Returns a token stream for +str+, for testing
-
- def self.tokenize(str)
- parser = new
- parser.tokenize str
- parser.tokens
- end
-
- ##
- # Creates a new Parser. See also ::parse
+module RDoc
+ class Markup
+ ##
+ # A recursive-descent parser for RDoc markup.
+ #
+ # The parser tokenizes an input string then parses the tokens into a Document.
+ # Documents can be converted into output formats by writing a visitor like
+ # RDoc::Markup::ToHTML.
+ #
+ # The parser only handles the block-level constructs Paragraph, List,
+ # ListItem, Heading, Verbatim, BlankLine, Rule and BlockQuote.
+ # Inline markup such as \+blah\+ is handled separately by
+ # RDoc::Markup::InlineParser.
+ #
+ # To see what markup the Parser implements read RDoc. To see how to use
+ # RDoc markup to format text in your program read RDoc::Markup.
+
+ class Parser
+
+ include Text
+
+ ##
+ # List token types
+
+ LIST_TOKENS = [
+ :BULLET,
+ :LABEL,
+ :LALPHA,
+ :NOTE,
+ :NUMBER,
+ :UALPHA,
+ ]
+
+ ##
+ # Parser error subclass
+
+ class Error < RuntimeError; end
+
+ ##
+ # Raised when the parser is unable to handle the given markup
+
+ class ParseError < Error; end
+
+ ##
+ # Enables display of debugging information
+
+ attr_accessor :debug
+
+ ##
+ # Token accessor
+
+ attr_reader :tokens
+
+ ##
+ # Parses +str+ into a Document.
+ #
+ # Use RDoc::Markup#parse instead of this method.
+
+ def self.parse(str)
+ parser = new
+ parser.tokenize str
+ doc = Markup::Document.new
+ parser.parse doc
+ end
- def initialize
- @binary_input = nil
- @current_token = nil
- @debug = false
- @s = nil
- @tokens = []
- end
+ ##
+ # Returns a token stream for +str+, for testing
- ##
- # Builds a Heading of +level+
+ def self.tokenize(str)
+ parser = new
+ parser.tokenize str
+ parser.tokens
+ end
- def build_heading(level)
- type, text, = get
+ ##
+ # Creates a new Parser. See also ::parse
- text = case type
- when :TEXT
- skip :NEWLINE
- text
- else
- unget
- ''
- end
+ def initialize
+ @binary_input = nil
+ @current_token = nil
+ @debug = false
+ @s = nil
+ @tokens = []
+ end
- RDoc::Markup::Heading.new level, text
- end
+ ##
+ # Builds a Heading of +level+
- ##
- # Builds a List flush to +margin+
+ def build_heading(level)
+ type, text, = get
- def build_list(margin)
- p :list_start => margin if @debug
+ text = case type
+ when :TEXT
+ skip :NEWLINE
+ text
+ else
+ unget
+ ''
+ end
- list = RDoc::Markup::List.new
- label = nil
+ Markup::Heading.new level, text
+ end
- until @tokens.empty? do
- type, data, column, = get
+ ##
+ # Builds a List flush to +margin+
- case type
- when *LIST_TOKENS
- if column < margin || (list.type && list.type != type)
- unget
- break
- end
+ def build_list(margin)
+ p :list_start => margin if @debug
- list.type = type
- peek_type, _, column, = peek_token
+ list = Markup::List.new
+ label = nil
- case type
- when :NOTE, :LABEL
- label = [] unless label
+ until @tokens.empty? do
+ type, data, column, = get
- if peek_type == :NEWLINE
- # description not on the same line as LABEL/NOTE
- # skip the trailing newline & any blank lines below
- while peek_type == :NEWLINE
- get
- peek_type, _, column, = peek_token
+ case type
+ when *LIST_TOKENS
+ if column < margin || (list.type && list.type != type)
+ unget
+ break
end
- # we may be:
- # - at end of stream
- # - at a column < margin:
- # [text]
- # blah blah blah
- # - at the same column, but with a different type of list item
- # [text]
- # * blah blah
- # - at the same column, with the same type of list item
- # [one]
- # [two]
- # In all cases, we have an empty description.
- # In the last case only, we continue.
- if peek_type.nil? || column < margin
- empty = true
- elsif column == margin
- case peek_type
- when type
- empty = :continue
- when *LIST_TOKENS
- empty = true
- else
- empty = false
+ list.type = type
+ peek_type, _, column, = peek_token
+
+ case type
+ when :NOTE, :LABEL
+ label = [] unless label
+
+ if peek_type == :NEWLINE
+ # description not on the same line as LABEL/NOTE
+ # skip the trailing newline & any blank lines below
+ while peek_type == :NEWLINE
+ get
+ peek_type, _, column, = peek_token
+ end
+
+ # we may be:
+ # - at end of stream
+ # - at a column < margin:
+ # [text]
+ # blah blah blah
+ # - at the same column, but with a different type of list item
+ # [text]
+ # * blah blah
+ # - at the same column, with the same type of list item
+ # [one]
+ # [two]
+ # In all cases, we have an empty description.
+ # In the last case only, we continue.
+ if peek_type.nil? || column < margin
+ empty = true
+ elsif column == margin
+ case peek_type
+ when type
+ empty = :continue
+ when *LIST_TOKENS
+ empty = true
+ else
+ empty = false
+ end
+ else
+ empty = false
+ end
+
+ if empty
+ label << data
+ next if empty == :continue
+ break
+ end
end
else
- empty = false
+ data = nil
end
- if empty
- label << data
- next if empty == :continue
- break
+ if label
+ data = label << data
+ label = nil
end
+
+ list_item = Markup::ListItem.new data
+ parse list_item, column
+ list << list_item
+
+ else
+ unget
+ break
end
- else
- data = nil
end
- if label
- data = label << data
- label = nil
- end
+ p :list_end => margin if @debug
- list_item = RDoc::Markup::ListItem.new data
- parse list_item, column
- list << list_item
+ if list.empty?
+ return nil unless label
+ return nil unless [:LABEL, :NOTE].include? list.type
- else
- unget
- break
- end
- end
+ list_item = Markup::ListItem.new label, Markup::BlankLine.new
+ list << list_item
+ end
- p :list_end => margin if @debug
+ list
+ end
- if list.empty?
- return nil unless label
- return nil unless [:LABEL, :NOTE].include? list.type
+ ##
+ # Builds a Paragraph that is flush to +margin+
- list_item = RDoc::Markup::ListItem.new label, RDoc::Markup::BlankLine.new
- list << list_item
- end
+ def build_paragraph(margin)
+ p :paragraph_start => margin if @debug
- list
- end
+ paragraph = Markup::Paragraph.new
- ##
- # Builds a Paragraph that is flush to +margin+
+ until @tokens.empty? do
+ type, data, column, = get
- def build_paragraph(margin)
- p :paragraph_start => margin if @debug
+ if type == :TEXT and column == margin
+ paragraph << data
- paragraph = RDoc::Markup::Paragraph.new
+ break if peek_token.first == :BREAK
- until @tokens.empty? do
- type, data, column, = get
+ data << ' ' if skip :NEWLINE and /#{SPACE_SEPARATED_LETTER_CLASS}\z/o.match?(data)
+ else
+ unget
+ break
+ end
+ end
- if type == :TEXT and column == margin
- paragraph << data
+ paragraph.parts.last.sub!(/ \z/, '') # cleanup
- break if peek_token.first == :BREAK
+ p :paragraph_end => margin if @debug
- data << ' ' if skip :NEWLINE and /#{SPACE_SEPARATED_LETTER_CLASS}\z/o.match?(data)
- else
- unget
- break
+ paragraph
end
- end
- paragraph.parts.last.sub!(/ \z/, '') # cleanup
+ ##
+ # Builds a Verbatim that is indented from +margin+.
+ #
+ # The verbatim block is shifted left (the least indented lines start in
+ # column 0). Each part of the verbatim is one line of text, always
+ # terminated by a newline. Blank lines always consist of a single newline
+ # character, and there is never a single newline at the end of the verbatim.
- p :paragraph_end => margin if @debug
-
- paragraph
- end
+ def build_verbatim(margin)
+ p :verbatim_begin => margin if @debug
+ verbatim = Markup::Verbatim.new
- ##
- # Builds a Verbatim that is indented from +margin+.
- #
- # The verbatim block is shifted left (the least indented lines start in
- # column 0). Each part of the verbatim is one line of text, always
- # terminated by a newline. Blank lines always consist of a single newline
- # character, and there is never a single newline at the end of the verbatim.
-
- def build_verbatim(margin)
- p :verbatim_begin => margin if @debug
- verbatim = RDoc::Markup::Verbatim.new
+ min_indent = nil
+ generate_leading_spaces = true
+ line = ''.dup
- min_indent = nil
- generate_leading_spaces = true
- line = ''.dup
+ until @tokens.empty? do
+ type, data, column, = get
- until @tokens.empty? do
- type, data, column, = get
+ if type == :NEWLINE
+ line << data
+ verbatim << line
+ line = ''.dup
+ generate_leading_spaces = true
+ next
+ end
- if type == :NEWLINE
- line << data
- verbatim << line
- line = ''.dup
- generate_leading_spaces = true
- next
- end
+ if column <= margin
+ unget
+ break
+ end
- if column <= margin
- unget
- break
- end
+ if generate_leading_spaces
+ indent = column - margin
+ line << ' ' * indent
+ min_indent = indent if min_indent.nil? || indent < min_indent
+ generate_leading_spaces = false
+ end
- if generate_leading_spaces
- indent = column - margin
- line << ' ' * indent
- min_indent = indent if min_indent.nil? || indent < min_indent
- generate_leading_spaces = false
- end
+ case type
+ when :HEADER
+ line << '=' * data
+ _, _, peek_column, = peek_token
+ peek_column ||= column + data
+ indent = peek_column - column - data
+ line << ' ' * indent
+ when :RULE
+ width = 2 + data
+ line << '-' * width
+ _, _, peek_column, = peek_token
+ peek_column ||= column + width
+ indent = peek_column - column - width
+ line << ' ' * indent
+ when :BREAK, :TEXT
+ line << data
+ when :BLOCKQUOTE
+ line << '>>>'
+ peek_type, _, peek_column = peek_token
+ if peek_type != :NEWLINE and peek_column
+ line << ' ' * (peek_column - column - 3)
+ end
+ else # *LIST_TOKENS
+ list_marker = case type
+ when :BULLET then data
+ when :LABEL then "[#{data}]"
+ when :NOTE then "#{data}::"
+ else # :LALPHA, :NUMBER, :UALPHA
+ "#{data}."
+ end
+ line << list_marker
+ peek_type, _, peek_column = peek_token
+ unless peek_type == :NEWLINE
+ peek_column ||= column + list_marker.length
+ indent = peek_column - column - list_marker.length
+ line << ' ' * indent
+ end
+ end
- case type
- when :HEADER
- line << '=' * data
- _, _, peek_column, = peek_token
- peek_column ||= column + data
- indent = peek_column - column - data
- line << ' ' * indent
- when :RULE
- width = 2 + data
- line << '-' * width
- _, _, peek_column, = peek_token
- peek_column ||= column + width
- indent = peek_column - column - width
- line << ' ' * indent
- when :BREAK, :TEXT
- line << data
- when :BLOCKQUOTE
- line << '>>>'
- peek_type, _, peek_column = peek_token
- if peek_type != :NEWLINE and peek_column
- line << ' ' * (peek_column - column - 3)
end
- else # *LIST_TOKENS
- list_marker = case type
- when :BULLET then data
- when :LABEL then "[#{data}]"
- when :NOTE then "#{data}::"
- else # :LALPHA, :NUMBER, :UALPHA
- "#{data}."
- end
- line << list_marker
- peek_type, _, peek_column = peek_token
- unless peek_type == :NEWLINE
- peek_column ||= column + list_marker.length
- indent = peek_column - column - list_marker.length
- line << ' ' * indent
- end
- end
- end
+ verbatim << line << "\n" unless line.empty?
+ verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0
+ verbatim.normalize
- verbatim << line << "\n" unless line.empty?
- verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0
- verbatim.normalize
+ p :verbatim_end => margin if @debug
- p :verbatim_end => margin if @debug
+ verbatim
+ end
- verbatim
- end
+ ##
+ # Pulls the next token from the stream.
- ##
- # Pulls the next token from the stream.
+ def get
+ @current_token = @tokens.shift
+ p :get => @current_token if @debug
+ @current_token
+ end
- def get
- @current_token = @tokens.shift
- p :get => @current_token if @debug
- @current_token
- end
+ ##
+ # Parses the tokens into an array of RDoc::Markup::XXX objects,
+ # and appends them to the passed +parent+ RDoc::Markup::YYY object.
+ #
+ # Exits at the end of the token stream, or when it encounters a token
+ # in a column less than +indent+ (unless it is a NEWLINE).
+ #
+ # Returns +parent+.
+
+ def parse(parent, indent = 0)
+ p :parse_start => indent if @debug
+
+ until @tokens.empty? do
+ type, data, column, = get
+
+ case type
+ when :BREAK
+ parent << Markup::BlankLine.new
+ skip :NEWLINE, false
+ next
+ when :NEWLINE
+ # trailing newlines are skipped below, so this is a blank line
+ parent << Markup::BlankLine.new
+ skip :NEWLINE, false
+ next
+ end
- ##
- # Parses the tokens into an array of RDoc::Markup::XXX objects,
- # and appends them to the passed +parent+ RDoc::Markup::YYY object.
- #
- # Exits at the end of the token stream, or when it encounters a token
- # in a column less than +indent+ (unless it is a NEWLINE).
- #
- # Returns +parent+.
-
- def parse(parent, indent = 0)
- p :parse_start => indent if @debug
-
- until @tokens.empty? do
- type, data, column, = get
-
- case type
- when :BREAK
- parent << RDoc::Markup::BlankLine.new
- skip :NEWLINE, false
- next
- when :NEWLINE
- # trailing newlines are skipped below, so this is a blank line
- parent << RDoc::Markup::BlankLine.new
- skip :NEWLINE, false
- next
- end
+ # indentation change: break or verbatim
+ if column < indent
+ unget
+ break
+ elsif column > indent
+ unget
+ parent << build_verbatim(indent)
+ next
+ end
- # indentation change: break or verbatim
- if column < indent
- unget
- break
- elsif column > indent
- unget
- parent << build_verbatim(indent)
- next
- end
+ # indentation is the same
+ case type
+ when :HEADER
+ parent << build_heading(data)
+ when :RULE
+ parent << Markup::Rule.new(data)
+ skip :NEWLINE
+ when :TEXT
+ unget
+ parse_text parent, indent
+ when :BLOCKQUOTE
+ nil while (type, = get; type) and type != :NEWLINE
+ _, _, column, = peek_token
+ bq = Markup::BlockQuote.new
+ p :blockquote_start => [data, column] if @debug
+ parse bq, column
+ p :blockquote_end => indent if @debug
+ parent << bq
+ when *LIST_TOKENS
+ unget
+ parent << build_list(indent)
+ else
+ type, data, column, line = @current_token
+ raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
+ end
+ end
- # indentation is the same
- case type
- when :HEADER
- parent << build_heading(data)
- when :RULE
- parent << RDoc::Markup::Rule.new(data)
- skip :NEWLINE
- when :TEXT
- unget
- parse_text parent, indent
- when :BLOCKQUOTE
- nil while (type, = get; type) and type != :NEWLINE
- _, _, column, = peek_token
- bq = RDoc::Markup::BlockQuote.new
- p :blockquote_start => [data, column] if @debug
- parse bq, column
- p :blockquote_end => indent if @debug
- parent << bq
- when *LIST_TOKENS
- unget
- parent << build_list(indent)
- else
- type, data, column, line = @current_token
- raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
- end
- end
+ p :parse_end => indent if @debug
- p :parse_end => indent if @debug
+ parent
- parent
+ end
- end
+ ##
+ # Small hook that is overridden by RDoc::TomDoc
- ##
- # Small hook that is overridden by RDoc::TomDoc
+ def parse_text(parent, indent) # :nodoc:
+ parent << build_paragraph(indent)
+ end
- def parse_text(parent, indent) # :nodoc:
- parent << build_paragraph(indent)
- end
+ ##
+ # Returns the next token on the stream without modifying the stream
- ##
- # Returns the next token on the stream without modifying the stream
+ def peek_token
+ token = @tokens.first || []
+ p :peek => token if @debug
+ token
+ end
- def peek_token
- token = @tokens.first || []
- p :peek => token if @debug
- token
- end
+ ##
+ # A simple wrapper of StringScanner that is aware of the current column and lineno
- ##
- # A simple wrapper of StringScanner that is aware of the current column and lineno
+ class MyStringScanner
+ # :stopdoc:
- class MyStringScanner
- # :stopdoc:
+ def initialize(input)
+ @line = @column = 0
+ @s = StringScanner.new input
+ end
- def initialize(input)
- @line = @column = 0
- @s = StringScanner.new input
- end
+ def scan(re)
+ ret = @s.scan(re)
+ @column += ret.length if ret
+ ret
+ end
- def scan(re)
- ret = @s.scan(re)
- @column += ret.length if ret
- ret
- end
+ def unscan(s)
+ @s.pos -= s.bytesize
+ @column -= s.length
+ end
- def unscan(s)
- @s.pos -= s.bytesize
- @column -= s.length
- end
+ def pos
+ [@column, @line]
+ end
- def pos
- [@column, @line]
- end
+ def newline!
+ @column = 0
+ @line += 1
+ end
- def newline!
- @column = 0
- @line += 1
- end
+ def eos?
+ @s.eos?
+ end
- def eos?
- @s.eos?
- end
+ def matched
+ @s.matched
+ end
- def matched
- @s.matched
- end
+ def [](i)
+ @s[i]
+ end
- def [](i)
- @s[i]
- end
+ #:startdoc:
+ end
- #:startdoc:
- end
+ ##
+ # Creates the StringScanner
- ##
- # Creates the StringScanner
+ def setup_scanner(input)
+ @s = MyStringScanner.new input
+ end
- def setup_scanner(input)
- @s = MyStringScanner.new input
- end
+ ##
+ # Skips the next token if its type is +token_type+.
+ #
+ # Optionally raises an error if the next token is not of the expected type.
- ##
- # Skips the next token if its type is +token_type+.
- #
- # Optionally raises an error if the next token is not of the expected type.
-
- def skip(token_type, error = true)
- type, = get
- return unless type # end of stream
- return @current_token if token_type == type
- unget
- raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
- end
+ def skip(token_type, error = true)
+ type, = get
+ return unless type # end of stream
+ return @current_token if token_type == type
+ unget
+ raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
+ end
- ##
- # Turns text +input+ into a stream of tokens
-
- def tokenize(input)
- setup_scanner input
-
- until @s.eos? do
- pos = @s.pos
-
- # leading spaces will be reflected by the column of the next token
- # the only thing we loose are trailing spaces at the end of the file
- next if @s.scan(/ +/)
-
- # note: after BULLET, LABEL, etc.,
- # indent will be the column of the next non-newline token
-
- @tokens << case
- # [CR]LF => :NEWLINE
- when @s.scan(/\r?\n/)
- token = [:NEWLINE, @s.matched, *pos]
- @s.newline!
- token
- # === text => :HEADER then :TEXT
- when @s.scan(/(=+)(\s*)/)
- level = @s[1].length
- header = [:HEADER, level, *pos]
-
- if @s[2] =~ /^\r?\n/
- @s.unscan(@s[2])
- header
- else
- pos = @s.pos
- @s.scan(/.*/)
- @tokens << header
- [:TEXT, @s.matched.sub(/\r$/, ''), *pos]
- end
- # --- (at least 3) and nothing else on the line => :RULE
- when @s.scan(/(-{3,}) *\r?$/)
- [:RULE, @s[1].length - 2, *pos]
- # * or - followed by white space and text => :BULLET
- when @s.scan(/([*-]) +(\S)/)
- @s.unscan(@s[2])
- [:BULLET, @s[1], *pos]
- # A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER
- when @s.scan(/([a-z]|\d+)\. +(\S)/i)
- # FIXME if tab(s), the column will be wrong
- # either support tabs everywhere by first expanding them to
- # spaces, or assume that they will have been replaced
- # before (and provide a check for that at least in debug
- # mode)
- list_label = @s[1]
- @s.unscan(@s[2])
- list_type =
- case list_label
- when /[a-z]/ then :LALPHA
- when /[A-Z]/ then :UALPHA
- when /\d/ then :NUMBER
+ ##
+ # Turns text +input+ into a stream of tokens
+
+ def tokenize(input)
+ setup_scanner input
+
+ until @s.eos? do
+ pos = @s.pos
+
+ # leading spaces will be reflected by the column of the next token
+ # the only thing we loose are trailing spaces at the end of the file
+ next if @s.scan(/ +/)
+
+ # note: after BULLET, LABEL, etc.,
+ # indent will be the column of the next non-newline token
+
+ @tokens << case
+ # [CR]LF => :NEWLINE
+ when @s.scan(/\r?\n/)
+ token = [:NEWLINE, @s.matched, *pos]
+ @s.newline!
+ token
+ # === text => :HEADER then :TEXT
+ when @s.scan(/(=+)(\s*)/)
+ level = @s[1].length
+ header = [:HEADER, level, *pos]
+
+ if @s[2] =~ /^\r?\n/
+ @s.unscan(@s[2])
+ header
+ else
+ pos = @s.pos
+ @s.scan(/.*/)
+ @tokens << header
+ [:TEXT, @s.matched.sub(/\r$/, ''), *pos]
+ end
+ # --- (at least 3) and nothing else on the line => :RULE
+ when @s.scan(/(-{3,}) *\r?$/)
+ [:RULE, @s[1].length - 2, *pos]
+ # * or - followed by white space and text => :BULLET
+ when @s.scan(/([*-]) +(\S)/)
+ @s.unscan(@s[2])
+ [:BULLET, @s[1], *pos]
+ # A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER
+ when @s.scan(/([a-z]|\d+)\. +(\S)/i)
+ # FIXME if tab(s), the column will be wrong
+ # either support tabs everywhere by first expanding them to
+ # spaces, or assume that they will have been replaced
+ # before (and provide a check for that at least in debug
+ # mode)
+ list_label = @s[1]
+ @s.unscan(@s[2])
+ list_type =
+ case list_label
+ when /[a-z]/ then :LALPHA
+ when /[A-Z]/ then :UALPHA
+ when /\d/ then :NUMBER
+ else
+ raise ParseError, "BUG token #{list_label}"
+ end
+ [list_type, list_label, *pos]
+ # [text] followed by spaces or end of line => :LABEL
+ when @s.scan(/\[(.*?)\]( +|\r?$)/)
+ [:LABEL, @s[1], *pos]
+ # text:: followed by spaces or end of line => :NOTE
+ when @s.scan(/(.*?)::( +|\r?$)/)
+ [:NOTE, @s[1], *pos]
+ # >>> followed by end of line => :BLOCKQUOTE
+ when @s.scan(/>>> *(\w+)?$/)
+ if word = @s[1]
+ @s.unscan(word)
+ end
+ [:BLOCKQUOTE, word, *pos]
+ # anything else: :TEXT
else
- raise ParseError, "BUG token #{list_label}"
+ @s.scan(/(.*?)( )?\r?$/)
+ token = [:TEXT, @s[1], *pos]
+
+ if @s[2]
+ @tokens << token
+ [:BREAK, @s[2], pos[0] + @s[1].length, pos[1]]
+ else
+ token
+ end
end
- [list_type, list_label, *pos]
- # [text] followed by spaces or end of line => :LABEL
- when @s.scan(/\[(.*?)\]( +|\r?$)/)
- [:LABEL, @s[1], *pos]
- # text:: followed by spaces or end of line => :NOTE
- when @s.scan(/(.*?)::( +|\r?$)/)
- [:NOTE, @s[1], *pos]
- # >>> followed by end of line => :BLOCKQUOTE
- when @s.scan(/>>> *(\w+)?$/)
- if word = @s[1]
- @s.unscan(word)
- end
- [:BLOCKQUOTE, word, *pos]
- # anything else: :TEXT
- else
- @s.scan(/(.*?)( )?\r?$/)
- token = [:TEXT, @s[1], *pos]
-
- if @s[2]
- @tokens << token
- [:BREAK, @s[2], pos[0] + @s[1].length, pos[1]]
- else
- token
- end
- end
- end
+ end
- self
- end
+ self
+ end
- ##
- # Returns the current token to the token stream
+ ##
+ # Returns the current token to the token stream
- def unget
- token = @current_token
- p :unget => token if @debug
- raise Error, 'too many #ungets' if token == @tokens.first
- @tokens.unshift token if token
- end
+ def unget
+ token = @current_token
+ p :unget => token if @debug
+ raise Error, 'too many #ungets' if token == @tokens.first
+ @tokens.unshift token if token
+ end
+ end
+ end
end
diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb
index e30b5da9bb..9e2c6ed81e 100644
--- a/lib/rdoc/markup/pre_process.rb
+++ b/lib/rdoc/markup/pre_process.rb
@@ -1,317 +1,321 @@
# frozen_string_literal: true
-##
-# Handle common directives that can occur in a block of text:
-#
-# \:include: filename
-#
-# Directives can be escaped by preceding them with a backslash.
-#
-# RDoc plugin authors can register additional directives to be handled by
-# using RDoc::Markup::PreProcess::register.
-#
-# Any directive that is not built-in to RDoc (including those registered via
-# plugins) will be stored in the metadata hash on the CodeObject the comment
-# is attached to. See RDoc::Markup@Directives for the list of built-in
-# directives.
-
-class RDoc::Markup::PreProcess
-
- ##
- # An RDoc::Options instance that will be filled in with overrides from
- # directives
-
- attr_accessor :options
-
- ##
- # Adds a post-process handler for directives. The handler will be called
- # with the result RDoc::Comment (or text String) and the code object for the
- # comment (if any).
-
- def self.post_process(&block)
- @post_processors << block
- end
-
- ##
- # Registered post-processors
-
- def self.post_processors
- @post_processors
- end
-
- ##
- # Registers +directive+ as one handled by RDoc. If a block is given the
- # directive will be replaced by the result of the block, otherwise the
- # directive will be removed from the processed text.
- #
- # The block will be called with the directive name and the directive
- # parameter:
- #
- # RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|
- # # replace text, etc.
- # end
-
- def self.register(directive, &block)
- @registered[directive] = block
- end
-
- ##
- # Registered directives
-
- def self.registered
- @registered
- end
-
- ##
- # Clears all registered directives and post-processors
-
- def self.reset
- @post_processors = []
- @registered = {}
- end
-
- reset
-
- ##
- # Creates a new pre-processor for +input_file_name+ that will look for
- # included files in +include_path+
-
- def initialize(input_file_name, include_path)
- @input_file_name = input_file_name
- @include_path = include_path
- @options = nil
- end
-
- ##
- # Look for directives in the given +text+.
- #
- # Options that we don't handle are yielded. If the block returns false the
- # directive is restored to the text. If the block returns nil or no block
- # was given the directive is handled according to the registered directives.
- # If a String was returned the directive is replaced with the string.
- #
- # If no matching directive was registered the directive is restored to the
- # text.
- #
- # If +code_object+ is given and the directive is unknown then the
- # directive's parameter is set as metadata on the +code_object+. See
- # RDoc::CodeObject#metadata for details.
-
- def handle(text, code_object = nil, &block)
- if RDoc::Comment === text
- comment = text
- text = text.text
- end
-
- # regexp helper (square brackets for optional)
- # $1 $2 $3 $4 $5
- # [prefix][\]:directive:[spaces][param]newline
- text = text.gsub(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):([\w-]+):([ \t]*)(.+)?(\r?\n|$)/) do
- # skip something like ':toto::'
- next $& if $4.empty? and $5 and $5[0, 1] == ':'
-
- # skip if escaped
- next "#$1:#$3:#$4#$5\n" unless $2.empty?
-
- # This is not in handle_directive because I didn't want to pass another
- # argument into it
- if comment and $3 == 'markup'
- next "#{$1.strip}\n" unless $5
- comment.format = $5.downcase
- next "#{$1.strip}\n"
+module RDoc
+ class Markup
+ ##
+ # Handle common directives that can occur in a block of text:
+ #
+ # \:include: filename
+ #
+ # Directives can be escaped by preceding them with a backslash.
+ #
+ # RDoc plugin authors can register additional directives to be handled by
+ # using RDoc::Markup::PreProcess::register.
+ #
+ # Any directive that is not built-in to RDoc (including those registered via
+ # plugins) will be stored in the metadata hash on the CodeObject the comment
+ # is attached to. See RDoc::Markup@Directives for the list of built-in
+ # directives.
+
+ class PreProcess
+
+ ##
+ # An RDoc::Options instance that will be filled in with overrides from
+ # directives
+
+ attr_accessor :options
+
+ ##
+ # Adds a post-process handler for directives. The handler will be called
+ # with the result RDoc::Comment (or text String) and the code object for the
+ # comment (if any).
+
+ def self.post_process(&block)
+ @post_processors << block
end
- handle_directive $1, $3, $5, code_object, text.encoding, &block
- end
- if comment
- comment.text = text
- else
- comment = text
- end
+ ##
+ # Registered post-processors
- run_post_processes(comment, code_object)
+ def self.post_processors
+ @post_processors
+ end
- text
- end
+ ##
+ # Registers +directive+ as one handled by RDoc. If a block is given the
+ # directive will be replaced by the result of the block, otherwise the
+ # directive will be removed from the processed text.
+ #
+ # The block will be called with the directive name and the directive
+ # parameter:
+ #
+ # RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|
+ # # replace text, etc.
+ # end
+
+ def self.register(directive, &block)
+ @registered[directive] = block
+ end
- # Apply directives to a code object
+ ##
+ # Registered directives
- def run_pre_processes(comment_text, code_object, start_line_no, type)
- comment_text, directives = parse_comment(comment_text, start_line_no, type)
- directives.each do |directive, (param, line_no)|
- handle_directive('', directive, param, code_object)
- end
- if code_object.is_a?(RDoc::AnyMethod) && (call_seq, = directives['call-seq']) && call_seq
- code_object.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n")
- end
- format, = directives['markup']
- [comment_text, format]
- end
+ def self.registered
+ @registered
+ end
- # Perform post preocesses to a code object
+ ##
+ # Clears all registered directives and post-processors
- def run_post_processes(comment, code_object)
- self.class.post_processors.each do |handler|
- handler.call comment, code_object
- end
- end
+ def self.reset
+ @post_processors = []
+ @registered = {}
+ end
- # Parse comment and return [normalized_comment_text, directives_hash]
+ reset
- def parse_comment(text, line_no, type)
- RDoc::Comment.parse(text, @input_file_name, line_no, type) do |filename, prefix_indent|
- include_file(filename, prefix_indent, text.encoding)
- end
- end
+ ##
+ # Creates a new pre-processor for +input_file_name+ that will look for
+ # included files in +include_path+
- ##
- # Performs the actions described by +directive+ and its parameter +param+.
- #
- # +code_object+ is used for directives that operate on a class or module.
- # +prefix+ is used to ensure the replacement for handled directives is
- # correct. +encoding+ is used for the include directive.
- #
- # For a list of directives in RDoc see RDoc::Markup.
- #--
- # When 1.8.7 support is ditched prefix can be defaulted to ''
-
- def handle_directive(prefix, directive, param, code_object = nil,
- encoding = nil)
- blankline = "#{prefix.strip}\n"
- directive = directive.downcase
-
- case directive
- when 'arg', 'args'
- return "#{prefix}:#{directive}: #{param}\n" unless code_object && code_object.kind_of?(RDoc::AnyMethod)
-
- code_object.params = param
-
- blankline
- when 'category'
- if RDoc::Context === code_object
- section = code_object.add_section param
- code_object.temporary_section = section
- elsif RDoc::AnyMethod === code_object
- code_object.section_title = param
+ def initialize(input_file_name, include_path)
+ @input_file_name = input_file_name
+ @include_path = include_path
+ @options = nil
end
- blankline # ignore category if we're not on an RDoc::Context
- when 'doc'
- return blankline unless code_object
- code_object.document_self = true
- code_object.force_documentation = true
-
- blankline
- when 'enddoc'
- return blankline unless code_object
- code_object.done_documenting = true
-
- blankline
- when 'include'
- filename = param.split(' ', 2).first
- include_file filename, prefix, encoding
- when 'nodoc'
- return blankline unless code_object
- code_object.document_self = nil # notify nodoc
- code_object.document_children = param !~ /all/i
+ ##
+ # Look for directives in the given +text+.
+ #
+ # Options that we don't handle are yielded. If the block returns false the
+ # directive is restored to the text. If the block returns nil or no block
+ # was given the directive is handled according to the registered directives.
+ # If a String was returned the directive is replaced with the string.
+ #
+ # If no matching directive was registered the directive is restored to the
+ # text.
+ #
+ # If +code_object+ is given and the directive is unknown then the
+ # directive's parameter is set as metadata on the +code_object+. See
+ # RDoc::CodeObject#metadata for details.
+
+ def handle(text, code_object = nil, &block)
+ if Comment === text
+ comment = text
+ text = text.text
+ end
- blankline
- when 'notnew', 'not_new', 'not-new'
- return blankline unless RDoc::AnyMethod === code_object
+ # regexp helper (square brackets for optional)
+ # $1 $2 $3 $4 $5
+ # [prefix][\]:directive:[spaces][param]newline
+ text = text.gsub(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):([\w-]+):([ \t]*)(.+)?(\r?\n|$)/) do
+ # skip something like ':toto::'
+ next $& if $4.empty? and $5 and $5[0, 1] == ':'
+
+ # skip if escaped
+ next "#$1:#$3:#$4#$5\n" unless $2.empty?
+
+ # This is not in handle_directive because I didn't want to pass another
+ # argument into it
+ if comment and $3 == 'markup'
+ next "#{$1.strip}\n" unless $5
+ comment.format = $5.downcase
+ next "#{$1.strip}\n"
+ end
+ handle_directive $1, $3, $5, code_object, text.encoding, &block
+ end
- code_object.dont_rename_initialize = true
+ if comment
+ comment.text = text
+ else
+ comment = text
+ end
- blankline
- when 'startdoc'
- return blankline unless code_object
+ run_post_processes(comment, code_object)
- code_object.start_doc
- code_object.force_documentation = true
+ text
+ end
- blankline
- when 'stopdoc'
- return blankline unless code_object
+ # Apply directives to a code object
- code_object.stop_doc
+ def run_pre_processes(comment_text, code_object, start_line_no, type)
+ comment_text, directives = parse_comment(comment_text, start_line_no, type)
+ directives.each do |directive, (param, line_no)|
+ handle_directive('', directive, param, code_object)
+ end
+ if code_object.is_a?(AnyMethod) && (call_seq, = directives['call-seq']) && call_seq
+ code_object.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n")
+ end
+ format, = directives['markup']
+ [comment_text, format]
+ end
- blankline
- when 'yield', 'yields'
- return blankline unless code_object
- # remove parameter &block
- code_object.params = code_object.params.sub(/,?\s*&\w+/, '') if code_object.params
+ # Perform post preocesses to a code object
- code_object.block_params = param || ''
+ def run_post_processes(comment, code_object)
+ self.class.post_processors.each do |handler|
+ handler.call comment, code_object
+ end
+ end
- blankline
- else
- result = yield directive, param if block_given?
+ # Parse comment and return [normalized_comment_text, directives_hash]
- case result
- when nil
- code_object.metadata[directive] = param if code_object
+ def parse_comment(text, line_no, type)
+ Comment.parse(text, @input_file_name, line_no, type) do |filename, prefix_indent|
+ include_file(filename, prefix_indent, text.encoding)
+ end
+ end
- if RDoc::Markup::PreProcess.registered.include? directive
- handler = RDoc::Markup::PreProcess.registered[directive]
- result = handler.call directive, param if handler
+ ##
+ # Performs the actions described by +directive+ and its parameter +param+.
+ #
+ # +code_object+ is used for directives that operate on a class or module.
+ # +prefix+ is used to ensure the replacement for handled directives is
+ # correct. +encoding+ is used for the include directive.
+ #
+ # For a list of directives in RDoc see RDoc::Markup.
+ #--
+ # When 1.8.7 support is ditched prefix can be defaulted to ''
+
+ def handle_directive(prefix, directive, param, code_object = nil,
+ encoding = nil)
+ blankline = "#{prefix.strip}\n"
+ directive = directive.downcase
+
+ case directive
+ when 'arg', 'args'
+ return "#{prefix}:#{directive}: #{param}\n" unless code_object && code_object.kind_of?(AnyMethod)
+
+ code_object.params = param
+
+ blankline
+ when 'category'
+ if Context === code_object
+ section = code_object.add_section param
+ code_object.temporary_section = section
+ elsif AnyMethod === code_object
+ code_object.section_title = param
+ end
+
+ blankline # ignore category if we're not on an RDoc::Context
+ when 'doc'
+ return blankline unless code_object
+ code_object.document_self = true
+ code_object.force_documentation = true
+
+ blankline
+ when 'enddoc'
+ return blankline unless code_object
+ code_object.done_documenting = true
+
+ blankline
+ when 'include'
+ filename = param.split(' ', 2).first
+ include_file filename, prefix, encoding
+ when 'nodoc'
+ return blankline unless code_object
+ code_object.document_self = nil # notify nodoc
+ code_object.document_children = param !~ /all/i
+
+ blankline
+ when 'notnew', 'not_new', 'not-new'
+ return blankline unless AnyMethod === code_object
+
+ code_object.dont_rename_initialize = true
+
+ blankline
+ when 'startdoc'
+ return blankline unless code_object
+
+ code_object.start_doc
+ code_object.force_documentation = true
+
+ blankline
+ when 'stopdoc'
+ return blankline unless code_object
+
+ code_object.stop_doc
+
+ blankline
+ when 'yield', 'yields'
+ return blankline unless code_object
+ # remove parameter &block
+ code_object.params = code_object.params.sub(/,?\s*&\w+/, '') if code_object.params
+
+ code_object.block_params = param || ''
+
+ blankline
else
- result = "#{prefix}:#{directive}: #{param}\n"
+ result = yield directive, param if block_given?
+
+ case result
+ when nil
+ code_object.metadata[directive] = param if code_object
+
+ if Markup::PreProcess.registered.include? directive
+ handler = Markup::PreProcess.registered[directive]
+ result = handler.call directive, param if handler
+ else
+ result = "#{prefix}:#{directive}: #{param}\n"
+ end
+ when false
+ result = "#{prefix}:#{directive}: #{param}\n"
+ end
+
+ result
end
- when false
- result = "#{prefix}:#{directive}: #{param}\n"
end
- result
- end
- end
+ ##
+ # Handles the :include: _filename_ directive.
+ #
+ # If the first line of the included file starts with '#', and contains
+ # an encoding information in the form 'coding:' or 'coding=', it is
+ # removed.
+ #
+ # If all lines in the included file start with a '#', this leading '#'
+ # is removed before inclusion. The included content is indented like
+ # the :include: directive.
+ #--
+ # so all content will be verbatim because of the likely space after '#'?
+ # TODO shift left the whole file content in that case
+ # TODO comment stop/start #-- and #++ in included file must be processed here
+
+ def include_file(name, indent, encoding)
+ full_name = find_include_file name
+
+ unless full_name
+ warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
+ return ''
+ end
- ##
- # Handles the :include: _filename_ directive.
- #
- # If the first line of the included file starts with '#', and contains
- # an encoding information in the form 'coding:' or 'coding=', it is
- # removed.
- #
- # If all lines in the included file start with a '#', this leading '#'
- # is removed before inclusion. The included content is indented like
- # the :include: directive.
- #--
- # so all content will be verbatim because of the likely space after '#'?
- # TODO shift left the whole file content in that case
- # TODO comment stop/start #-- and #++ in included file must be processed here
-
- def include_file(name, indent, encoding)
- full_name = find_include_file name
-
- unless full_name
- warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
- return ''
- end
+ content = Encoding.read_file full_name, encoding, true
+ content = Encoding.remove_magic_comment content
- content = RDoc::Encoding.read_file full_name, encoding, true
- content = RDoc::Encoding.remove_magic_comment content
+ # strip magic comment
+ content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
- # strip magic comment
- content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
+ # strip leading '#'s, but only if all lines start with them
+ if content =~ /^[^#]/
+ content.gsub(/^/, indent)
+ else
+ content.gsub(/^#?/, indent)
+ end
+ end
- # strip leading '#'s, but only if all lines start with them
- if content =~ /^[^#]/
- content.gsub(/^/, indent)
- else
- content.gsub(/^#?/, indent)
- end
- end
+ ##
+ # Look for the given file in the directory containing the current file,
+ # and then in each of the directories specified in the RDOC_INCLUDE path
- ##
- # Look for the given file in the directory containing the current file,
- # and then in each of the directories specified in the RDOC_INCLUDE path
+ def find_include_file(name)
+ to_search = [File.dirname(@input_file_name)].concat @include_path
+ to_search.each do |dir|
+ full_name = File.join(dir, name)
+ stat = File.stat(full_name) rescue next
+ return full_name if stat.readable?
+ end
+ nil
+ end
- def find_include_file(name)
- to_search = [File.dirname(@input_file_name)].concat @include_path
- to_search.each do |dir|
- full_name = File.join(dir, name)
- stat = File.stat(full_name) rescue next
- return full_name if stat.readable?
end
- nil
end
-
end
diff --git a/lib/rdoc/markup/rule.rb b/lib/rdoc/markup/rule.rb
index 5ff7475321..0df63c6a9d 100644
--- a/lib/rdoc/markup/rule.rb
+++ b/lib/rdoc/markup/rule.rb
@@ -1,20 +1,24 @@
# frozen_string_literal: true
-##
-# A horizontal rule with a weight
+module RDoc
+ class Markup
+ ##
+ # A horizontal rule with a weight
-class RDoc::Markup::Rule < Struct.new :weight
+ class Rule < Struct.new :weight
- ##
- # Calls #accept_rule on +visitor+
+ ##
+ # Calls #accept_rule on +visitor+
- def accept(visitor)
- visitor.accept_rule self
- end
+ def accept(visitor)
+ visitor.accept_rule self
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.group 2, '[rule:', ']' do
+ q.pp weight
+ end
+ end
- def pretty_print(q) # :nodoc:
- q.group 2, '[rule:', ']' do
- q.pp weight
end
end
-
end
diff --git a/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb
index 3f17bfd2d8..a470c29bfa 100644
--- a/lib/rdoc/markup/to_ansi.rb
+++ b/lib/rdoc/markup/to_ansi.rb
@@ -1,144 +1,148 @@
# frozen_string_literal: true
-##
-# Outputs RDoc markup with vibrant ANSI color!
+module RDoc
+ class Markup
+ ##
+ # Outputs RDoc markup with vibrant ANSI color!
+
+ class ToAnsi < Markup::ToRdoc
+
+ ##
+ # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color!
+
+ def initialize
+ super
+
+ @headings.clear
+ @headings[1] = ["\e[1;32m", "\e[m"] # bold
+ @headings[2] = ["\e[4;32m", "\e[m"] # underline
+ @headings[3] = ["\e[32m", "\e[m"] # just green
+ end
+
+ ##
+ # Maps attributes to ANSI sequences
+
+ ANSI_STYLE_CODES_ON = {
+ BOLD: 1,
+ TT: 7,
+ EM: 4,
+ STRIKE: 9
+ }
+
+ ANSI_STYLE_CODES_OFF = {
+ BOLD: 22,
+ TT: 27,
+ EM: 24,
+ STRIKE: 29
+ }
+
+ # Apply the given attributes by emitting ANSI sequences.
+ # Emitting attribute changes are deferred until new text is added and applied in batch.
+ # This method computes the necessary ANSI codes to transition from the
+ # current set of applied attributes to the new set of +attributes+.
+
+ def apply_attributes(attributes)
+ before = @applied_attributes
+ after = attributes.sort
+ return if before == after
+
+ if after.empty?
+ emit_inline("\e[m")
+ elsif !before.empty? && before.size > (before & after).size + 1
+ codes = after.map {|attr| ANSI_STYLE_CODES_ON[attr] }.compact
+ emit_inline("\e[#{[0, *codes].join(';')}m")
+ else
+ off_codes = (before - after).map {|attr| ANSI_STYLE_CODES_OFF[attr] }.compact
+ on_codes = (after - before).map {|attr| ANSI_STYLE_CODES_ON[attr] }.compact
+ emit_inline("\e[#{(off_codes + on_codes).join(';')}m")
+ end
+ @applied_attributes = attributes
+ end
+
+ def add_text(text)
+ attrs = @attributes.keys
+ if @applied_attributes != attrs
+ apply_attributes(attrs)
+ end
+ emit_inline(text)
+ end
+
+ def handle_inline(text)
+ @applied_attributes = []
+ res = super
+ res << "\e[m" unless @applied_attributes.empty?
+ @applied_attributes = []
+ res
+ end
+
+ ##
+ # Overrides indent width to ensure output lines up correctly.
+
+ def accept_list_item_end(list_item)
+ width = case @list_type.last
+ when :BULLET
+ 2
+ when :NOTE, :LABEL
+ if @prefix
+ @res << @prefix.strip
+ @prefix = nil
+ end
+
+ @res << "\n" unless res.length == 1
+ 2
+ else
+ bullet = @list_index.last.to_s
+ @list_index[-1] = @list_index.last.succ
+ bullet.length + 2
+ end
+
+ @indent -= width
+ end
+
+ ##
+ # Adds coloring to note and label list items
+
+ def accept_list_item_start(list_item)
+ bullet = case @list_type.last
+ when :BULLET
+ '*'
+ when :NOTE, :LABEL
+ labels = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ labels << ":\n" unless labels.empty?
+
+ labels
+ else
+ @list_index.last.to_s + '.'
+ end
+
+ case @list_type.last
+ when :NOTE, :LABEL
+ @indent += 2
+ @prefix = bullet + (' ' * @indent)
+ else
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+
+ width = bullet.gsub(/\e\[[\d;]*m/, '').length + 1
+
+ @indent += width
+ end
+ end
+
+ def calculate_text_width(text)
+ text.gsub(/\e\[[\d;]*m/, '').size
+ end
+
+ ##
+ # Starts accepting with a reset screen
+
+ def start_accepting
+ super
+
+ @res = ["\e[0m"]
+ end
-class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
-
- ##
- # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color!
-
- def initialize
- super
-
- @headings.clear
- @headings[1] = ["\e[1;32m", "\e[m"] # bold
- @headings[2] = ["\e[4;32m", "\e[m"] # underline
- @headings[3] = ["\e[32m", "\e[m"] # just green
- end
-
- ##
- # Maps attributes to ANSI sequences
-
- ANSI_STYLE_CODES_ON = {
- BOLD: 1,
- TT: 7,
- EM: 4,
- STRIKE: 9
- }
-
- ANSI_STYLE_CODES_OFF = {
- BOLD: 22,
- TT: 27,
- EM: 24,
- STRIKE: 29
- }
-
- # Apply the given attributes by emitting ANSI sequences.
- # Emitting attribute changes are deferred until new text is added and applied in batch.
- # This method computes the necessary ANSI codes to transition from the
- # current set of applied attributes to the new set of +attributes+.
-
- def apply_attributes(attributes)
- before = @applied_attributes
- after = attributes.sort
- return if before == after
-
- if after.empty?
- emit_inline("\e[m")
- elsif !before.empty? && before.size > (before & after).size + 1
- codes = after.map {|attr| ANSI_STYLE_CODES_ON[attr] }.compact
- emit_inline("\e[#{[0, *codes].join(';')}m")
- else
- off_codes = (before - after).map {|attr| ANSI_STYLE_CODES_OFF[attr] }.compact
- on_codes = (after - before).map {|attr| ANSI_STYLE_CODES_ON[attr] }.compact
- emit_inline("\e[#{(off_codes + on_codes).join(';')}m")
- end
- @applied_attributes = attributes
- end
-
- def add_text(text)
- attrs = @attributes.keys
- if @applied_attributes != attrs
- apply_attributes(attrs)
- end
- emit_inline(text)
- end
-
- def handle_inline(text)
- @applied_attributes = []
- res = super
- res << "\e[m" unless @applied_attributes.empty?
- @applied_attributes = []
- res
- end
-
- ##
- # Overrides indent width to ensure output lines up correctly.
-
- def accept_list_item_end(list_item)
- width = case @list_type.last
- when :BULLET
- 2
- when :NOTE, :LABEL
- if @prefix
- @res << @prefix.strip
- @prefix = nil
- end
-
- @res << "\n" unless res.length == 1
- 2
- else
- bullet = @list_index.last.to_s
- @list_index[-1] = @list_index.last.succ
- bullet.length + 2
- end
-
- @indent -= width
- end
-
- ##
- # Adds coloring to note and label list items
-
- def accept_list_item_start(list_item)
- bullet = case @list_type.last
- when :BULLET
- '*'
- when :NOTE, :LABEL
- labels = Array(list_item.label).map do |label|
- attributes(label).strip
- end.join "\n"
-
- labels << ":\n" unless labels.empty?
-
- labels
- else
- @list_index.last.to_s + '.'
- end
-
- case @list_type.last
- when :NOTE, :LABEL
- @indent += 2
- @prefix = bullet + (' ' * @indent)
- else
- @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
-
- width = bullet.gsub(/\e\[[\d;]*m/, '').length + 1
-
- @indent += width
end
end
-
- def calculate_text_width(text)
- text.gsub(/\e\[[\d;]*m/, '').size
- end
-
- ##
- # Starts accepting with a reset screen
-
- def start_accepting
- super
-
- @res = ["\e[0m"]
- end
-
end
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
index d6b5cd6c86..0aaf70e6f0 100644
--- a/lib/rdoc/markup/to_bs.rb
+++ b/lib/rdoc/markup/to_bs.rb
@@ -1,86 +1,90 @@
# frozen_string_literal: true
-##
-# Outputs RDoc markup with hot backspace action! You will probably need a
-# pager to use this output format.
-#
-# This formatter won't work on 1.8.6 because it lacks String#chars.
+module RDoc
+ class Markup
+ ##
+ # Outputs RDoc markup with hot backspace action! You will probably need a
+ # pager to use this output format.
+ #
+ # This formatter won't work on 1.8.6 because it lacks String#chars.
-class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
+ class ToBs < Markup::ToRdoc
- ##
- # Returns a new ToBs that is ready for hot backspace action!
+ ##
+ # Returns a new ToBs that is ready for hot backspace action!
- def initialize
- super
+ def initialize
+ super
- @in_b = false
- @in_em = false
- end
-
- def handle_inline(text)
- initial_style = []
- initial_style << :BOLD if @in_b
- initial_style << :EM if @in_em
- super(text, initial_style)
- end
+ @in_b = false
+ @in_em = false
+ end
- def add_text(text)
- attrs = @attributes.keys
- if attrs.include? :BOLD
- styled = +''
- text.chars.each do |c|
- styled << "#{c}\b#{c}"
+ def handle_inline(text)
+ initial_style = []
+ initial_style << :BOLD if @in_b
+ initial_style << :EM if @in_em
+ super(text, initial_style)
end
- text = styled
- elsif attrs.include? :EM
- styled = +''
- text.chars.each do |c|
- styled << "_\b#{c}"
+
+ def add_text(text)
+ attrs = @attributes.keys
+ if attrs.include? :BOLD
+ styled = +''
+ text.chars.each do |c|
+ styled << "#{c}\b#{c}"
+ end
+ text = styled
+ elsif attrs.include? :EM
+ styled = +''
+ text.chars.each do |c|
+ styled << "_\b#{c}"
+ end
+ text = styled
+ end
+ emit_inline(text)
end
- text = styled
- end
- emit_inline(text)
- end
- ##
- # Makes heading text bold.
+ ##
+ # Makes heading text bold.
- def accept_heading(heading)
- use_prefix or @res << ' ' * @indent
- @res << @headings[heading.level][0]
- @in_b = true
- @res << attributes(heading.text)
- @in_b = false
- @res << @headings[heading.level][1]
- @res << "\n"
- end
+ def accept_heading(heading)
+ use_prefix or @res << ' ' * @indent
+ @res << @headings[heading.level][0]
+ @in_b = true
+ @res << attributes(heading.text)
+ @in_b = false
+ @res << @headings[heading.level][1]
+ @res << "\n"
+ end
- ##
- # Prepares the visitor for consuming +list_item+
+ ##
+ # Prepares the visitor for consuming +list_item+
- def accept_list_item_start(list_item)
- type = @list_type.last
+ def accept_list_item_start(list_item)
+ type = @list_type.last
- case type
- when :NOTE, :LABEL
- bullets = Array(list_item.label).map do |label|
- attributes(label).strip
- end.join "\n"
+ case type
+ when :NOTE, :LABEL
+ bullets = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
- bullets << ":\n" unless bullets.empty?
+ bullets << ":\n" unless bullets.empty?
- @prefix = ' ' * @indent
- @indent += 2
- @prefix << bullets + (' ' * @indent)
- else
- bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
- @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
- width = bullet.length + 1
- @indent += width
- end
- end
+ @prefix = ' ' * @indent
+ @indent += 2
+ @prefix << bullets + (' ' * @indent)
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+ width = bullet.length + 1
+ @indent += width
+ end
+ end
- def calculate_text_width(text)
- text.gsub(/_\x08/, '').gsub(/\x08./, '').size
+ def calculate_text_width(text)
+ text.gsub(/_\x08/, '').gsub(/\x08./, '').size
+ end
+ end
end
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 95814f2023..474a74ccc3 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -3,680 +3,684 @@
require 'cgi/util' unless defined?(CGI::EscapeExt)
require 'prism'
-##
-# Outputs RDoc markup as HTML.
-
-class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
-
- include RDoc::Text
-
- # :section: Utilities
-
- ##
- # Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
-
- LIST_TYPE_TO_HTML = {
- :BULLET => ['')
- super
- emit_inline('')
- end
+ def handle_TT(code)
+ emit_inline('')
+ super
+ emit_inline('')
+ end
- def handle_STRIKE(nodes)
- emit_inline('" - def accept_block_quote(block_quote) - @res << "\n\n" - end + ## + # Adds +paragraph+ to the output + + def accept_paragraph(paragraph) + @res << "\n" + block_quote.parts.each do |part| + part.accept self + end - block_quote.parts.each do |part| - part.accept self - end + @res << "\n" + end - @res << "
" + text = paragraph.text @hard_break + text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K(?:\r?\n)+(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) { + defined?($2) && ' ' + } + @res << to_html(text) + @res << "
\n" + end - ## - # Adds +paragraph+ to the output - - def accept_paragraph(paragraph) - @res << "\n" - text = paragraph.text @hard_break - text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K(?:\r?\n)+(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) { - defined?($2) && ' ' - } - @res << to_html(text) - @res << "
\n" - end + # Generate syntax highlighted html for ruby-like text. - # Generate syntax highlighted html for ruby-like text. + def parsable_text_to_html(text) + tokens = ::RDoc::Parser::RubyColorizer.colorize(text) + result = TokenStream.to_html tokens + result = result + "\n" unless result.end_with?("\n") + result + end - def parsable_text_to_html(text) - tokens = RDoc::Parser::RubyColorizer.colorize(text) - result = RDoc::TokenStream.to_html tokens - result = result + "\n" unless result.end_with?("\n") - result - end + ## + # Adds +verbatim+ to the output - ## - # Adds +verbatim+ to the output - - def accept_verbatim(verbatim) - text = verbatim.text.rstrip - format = verbatim.format - - # Apply Ruby syntax highlighting if - # - explicitly marked as Ruby (via ruby? which accepts :ruby or :rb) - # - no format specified but the text is parseable as Ruby - # Otherwise, add language class when applicable and skip Ruby highlighting - if verbatim.ruby? || (format.nil? && parseable?(text)) - content = parsable_text_to_html(text) - klass = ' class="ruby"' - else - content = CGI.escapeHTML text - klass = " class=\"#{format}\"" if format - end + def accept_verbatim(verbatim) + text = verbatim.text.rstrip + format = verbatim.format - if @pipe - @res << "\n#{CGI.escapeHTML text}\n\n"
- else
- @res << "\n#{content}\n"
- end
- end
+ # Apply Ruby syntax highlighting if
+ # - explicitly marked as Ruby (via ruby? which accepts :ruby or :rb)
+ # - no format specified but the text is parseable as Ruby
+ # Otherwise, add language class when applicable and skip Ruby highlighting
+ if verbatim.ruby? || (format.nil? && parseable?(text))
+ content = parsable_text_to_html(text)
+ klass = ' class="ruby"'
+ else
+ content = CGI.escapeHTML text
+ klass = " class=\"#{format}\"" if format
+ end
- ##
- # Adds +rule+ to the output
+ if @pipe
+ @res << "\n#{CGI.escapeHTML text}\n\n"
+ else
+ @res << "\n#{content}\n"
+ end
+ end
- def accept_rule(rule)
- @res << "| ' << to_html(text) << " | \n" - end - @res << "
|---|
| ' << to_html(text) << " | \n" - end - @res << "
| ' << to_html(text) << " | \n" + end + @res << "
|---|
| ' << to_html(text) << " | \n" + end + @res << "
#{convert_string(context_name)}" if context_name
- text ||= label
- code = false
- else
- text ||= convert_string(display)
- end
-
- link(name, text, code, rdoc_ref: rdoc_ref)
- end
-
- ##
- # We're invoked when any text matches the CROSSREF pattern. If we find the
- # corresponding reference, generate a link. If the name we're looking for
- # contains no punctuation, we look for it up the module/class chain. For
- # example, ToHtml is found, even without the RDoc::Markup:: prefix,
- # because we look for it in module Markup first.
-
- def handle_regexp_CROSSREF(name)
- return convert_string(name) if in_tidylink_label?
- return name if @autolink_excluded_words&.include?(name)
-
- return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
-
- unless @hyperlink_all
- # This ensures that words entirely consisting of lowercase letters will
- # not have cross-references generated (to suppress lots of erroneous
- # cross-references to "new" in text, for instance)
- return name if name =~ /\A[a-z]*\z/
- end
- cross_reference(name, rdoc_ref: false) || convert_string(name)
- end
-
- ##
- # Handles rdoc-ref: scheme links and allows RDoc::Markup::ToHtml to
- # handle other schemes.
-
- def handle_regexp_HYPERLINK(url)
- return convert_string(url) if in_tidylink_label?
-
- case url
- when /\Ardoc-ref:/
- ref = $'
- cross_reference(ref, rdoc_ref: true) || convert_string(ref)
- else
- super
- end
- end
-
- ##
- # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
- # For the rdoc-ref scheme the cross-reference will be looked up and the
- # given name will be used.
- #
- # All other contents are handled by
- # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]
-
- def handle_regexp_RDOCLINK(url)
- case url
- when /\Ardoc-ref:/
- if in_tidylink_label?
- convert_string(url)
- else
- ref = $'
- cross_reference(ref, rdoc_ref: true) || convert_string(ref)
+ @cross_reference = CrossReference.new @context
end
- else
- super
- end
- end
- ##
- # Generates links for rdoc-ref: scheme URLs and allows
- # RDoc::Markup::ToHtml to handle other schemes.
+ # :nodoc:
+ def init_link_notation_regexp_handlings
+ add_regexp_handling_RDOCLINK
- def gen_url(url, text)
- if url =~ /\Ardoc-ref:/
- name = $'
- cross_reference(name, text, name == text, rdoc_ref: true) || text
- else
- super
- end
- end
-
- ##
- # Creates an HTML link to +name+ with the given +html_string+.
- # +html_string+ should be already escaped and may contain HTML tags.
- # Returns the link HTML string, or +nil+ if the reference could not be resolved.
+ # The crossref must be linked before tidylink because Klass.method[:sym]
+ # will be processed as a tidylink first and will be broken.
+ crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
+ @markup.add_regexp_handling crossref_re, :CROSSREF
+ end
- def link(name, html_string, code = true, rdoc_ref: false)
- if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
- name = $1
- label = $'
- end
+ ##
+ # Creates a link to the reference +name+ if the name exists. If +text+ is
+ # given it is used as the link text, otherwise +name+ is used.
+ # Returns +nil+ if the link target could not be resolved.
+
+ def cross_reference(name, text = nil, code = true, rdoc_ref: false)
+ # Strip '#' for link display text (e.g. #method shows as "method" in links)
+ display = !@show_hash && name.start_with?('#') ? name[1..] : name
+
+ if !display.end_with?('+@', '-@') && match = display.match(/(.*[^#:])?@(.*)/)
+ context_name = match[1]
+ label = convert_string(Text.decode_legacy_label(match[2]))
+ text ||= "#{label} at #{convert_string(context_name)}" if context_name
+ text ||= label
+ code = false
+ else
+ text ||= convert_string(display)
+ end
+
+ link(name, text, code, rdoc_ref: rdoc_ref)
+ end
- ref = @cross_reference.resolve name if name
+ ##
+ # We're invoked when any text matches the CROSSREF pattern. If we find the
+ # corresponding reference, generate a link. If the name we're looking for
+ # contains no punctuation, we look for it up the module/class chain. For
+ # example, ToHtml is found, even without the RDoc::Markup:: prefix,
+ # because we look for it in module Markup first.
+
+ def handle_regexp_CROSSREF(name)
+ return convert_string(name) if in_tidylink_label?
+ return name if @autolink_excluded_words&.include?(name)
+
+ return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
+
+ unless @hyperlink_all
+ # This ensures that words entirely consisting of lowercase letters will
+ # not have cross-references generated (to suppress lots of erroneous
+ # cross-references to "new" in text, for instance)
+ return name if name =~ /\A[a-z]*\z/
+ end
+ cross_reference(name, rdoc_ref: false) || convert_string(name)
+ end
- # Non-text source files (C, Ruby, etc.) don't get HTML pages generated,
- # so don't auto-link to them. Explicit rdoc-ref: links are still allowed.
- if !rdoc_ref && RDoc::TopLevel === ref && !ref.text?
- return
- end
+ ##
+ # Handles rdoc-ref: scheme links and allows RDoc::Markup::ToHtml to
+ # handle other schemes.
- if ref
- path = ref.as_href(@from_path)
+ def handle_regexp_HYPERLINK(url)
+ return convert_string(url) if in_tidylink_label?
- if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref)
- html_string = "#{html_string}"
+ case url
+ when /\Ardoc-ref:/
+ ref = $'
+ cross_reference(ref, rdoc_ref: true) || convert_string(ref)
+ else
+ super
+ end
end
- elsif name
- if rdoc_ref && @warn_missing_rdoc_ref
- puts "#{@from_path}: `rdoc-ref:#{name}` can't be resolved for `#{html_string}`"
+
+ ##
+ # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
+ # For the rdoc-ref scheme the cross-reference will be looked up and the
+ # given name will be used.
+ #
+ # All other contents are handled by
+ # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]
+
+ def handle_regexp_RDOCLINK(url)
+ case url
+ when /\Ardoc-ref:/
+ if in_tidylink_label?
+ convert_string(url)
+ else
+ ref = $'
+ cross_reference(ref, rdoc_ref: true) || convert_string(ref)
+ end
+ else
+ super
+ end
end
- return
- else
- # A bare label reference like @foo still produces a valid anchor link
- return unless label
- path = +""
- end
- if label
- # Decode legacy labels (e.g., "What-27s+Here" -> "What's Here")
- # then convert to GitHub-style anchor format
- decoded_label = RDoc::Text.decode_legacy_label(label)
- formatted_label = RDoc::Text.to_anchor(decoded_label)
-
- # Case 1: Path already has an anchor (e.g., method link)
- # Input: C1#method@label -> path="C1.html#method-i-m"
- # Output: C1.html#method-i-m-label
- if path =~ /#/
- path << "-#{formatted_label}"
-
- # Case 2: Label matches a section title
- # Input: C1@Section -> path="C1.html", section "Section" exists
- # Output: C1.html#section (uses section.aref for GitHub-style)
- elsif (section = ref&.sections&.find { |s| decoded_label == s.title })
- path << "##{section.aref}"
-
- # Case 3: Ref has an aref (class/module context)
- # Input: C1@heading -> path="C1.html", ref=C1 class
- # Output: C1.html#class-c1-heading
- elsif ref.respond_to?(:aref)
- path << "##{ref.aref}-#{formatted_label}"
-
- # Case 4: No context, just the label (e.g., TopLevel/file)
- # Input: README@section -> path="README_md.html"
- # Output: README_md.html#section
- else
- path << "##{formatted_label}"
+ ##
+ # Generates links for rdoc-ref: scheme URLs and allows
+ # RDoc::Markup::ToHtml to handle other schemes.
+
+ def gen_url(url, text)
+ if url =~ /\Ardoc-ref:/
+ name = $'
+ cross_reference(name, text, name == text, rdoc_ref: true) || text
+ else
+ super
+ end
end
- end
- "#{html_string}"
- end
+ ##
+ # Creates an HTML link to +name+ with the given +html_string+.
+ # +html_string+ should be already escaped and may contain HTML tags.
+ # Returns the link HTML string, or +nil+ if the reference could not be resolved.
+
+ def link(name, html_string, code = true, rdoc_ref: false)
+ if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
+ name = $1
+ label = $'
+ end
+
+ ref = @cross_reference.resolve name if name
+
+ # Non-text source files (C, Ruby, etc.) don't get HTML pages generated,
+ # so don't auto-link to them. Explicit rdoc-ref: links are still allowed.
+ if !rdoc_ref && TopLevel === ref && !ref.text?
+ return
+ end
+
+ if ref
+ path = ref.as_href(@from_path)
+
+ if code and CodeObject === ref and !(TopLevel === ref)
+ html_string = "#{html_string}"
+ end
+ elsif name
+ if rdoc_ref && @warn_missing_rdoc_ref
+ puts "#{@from_path}: `rdoc-ref:#{name}` can't be resolved for `#{html_string}`"
+ end
+ return
+ else
+ # A bare label reference like @foo still produces a valid anchor link
+ return unless label
+ path = +""
+ end
+
+ if label
+ # Decode legacy labels (e.g., "What-27s+Here" -> "What's Here")
+ # then convert to GitHub-style anchor format
+ decoded_label = Text.decode_legacy_label(label)
+ formatted_label = Text.to_anchor(decoded_label)
+
+ # Case 1: Path already has an anchor (e.g., method link)
+ # Input: C1#method@label -> path="C1.html#method-i-m"
+ # Output: C1.html#method-i-m-label
+ if path =~ /#/
+ path << "-#{formatted_label}"
+
+ # Case 2: Label matches a section title
+ # Input: C1@Section -> path="C1.html", section "Section" exists
+ # Output: C1.html#section (uses section.aref for GitHub-style)
+ elsif (section = ref&.sections&.find { |s| decoded_label == s.title })
+ path << "##{section.aref}"
+
+ # Case 3: Ref has an aref (class/module context)
+ # Input: C1@heading -> path="C1.html", ref=C1 class
+ # Output: C1.html#class-c1-heading
+ elsif ref.respond_to?(:aref)
+ path << "##{ref.aref}-#{formatted_label}"
+
+ # Case 4: No context, just the label (e.g., TopLevel/file)
+ # Input: README@section -> path="README_md.html"
+ # Output: README_md.html#section
+ else
+ path << "##{formatted_label}"
+ end
+ end
+
+ "#{html_string}"
+ end
- def handle_TT(code)
- emit_inline(tt_cross_reference(code) || "#{convert_string(code)}")
- end
+ def handle_TT(code)
+ emit_inline(tt_cross_reference(code) || "#{convert_string(code)}")
+ end
- # Applies additional special handling on top of the one defined in ToHtml.
- # When a tidy link is {Foo}[rdoc-ref:Foo], the label part is surrounded by .
- # TODO: reconsider this workaround.
- def apply_tidylink_label_special_handling(label, url)
- if url == "rdoc-ref:#{label}" && cross_reference(label)&.include?('')
- "#{convert_string(label)}"
- else
- super
- end
- end
+ # Applies additional special handling on top of the one defined in ToHtml.
+ # When a tidy link is {Foo}[rdoc-ref:Foo], the label part is surrounded by .
+ # TODO: reconsider this workaround.
+ def apply_tidylink_label_special_handling(label, url)
+ if url == "rdoc-ref:#{label}" && cross_reference(label)&.include?('')
+ "#{convert_string(label)}"
+ else
+ super
+ end
+ end
- # Handles cross-reference and suppressed-crossref inside tt tag.
- # Returns nil if code is not an existing cross-reference nor a suppressed-crossref.
- def tt_cross_reference(code)
- return if in_tidylink_label?
-
- crossref_regexp = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
- # REGEXP sometimes matches a string that starts with a backslash but is not a
- # suppressed cross-reference (for example, `\+`), so the backslash-removed
- # part needs to be checked against crossref_regexp.
- match = crossref_regexp.match(code.delete_prefix('\\'))
- return unless match && match.begin(1).zero?
- return unless match.post_match.match?(/\A[[:punct:]\s]*\z/)
-
- # cross_reference(file_page) may return a link without code tag.
- # We need to check it because this method shouldn't return an html text without code tag.
- if code.start_with?('\\')
- # Remove leading backslash if crossref exists
- "#{convert_string(code[1..])}" if cross_reference(code[1..])&.include?('')
- else
- html = cross_reference(code)
- html if html&.include?('')
+ # Handles cross-reference and suppressed-crossref inside tt tag.
+ # Returns nil if code is not an existing cross-reference nor a suppressed-crossref.
+ def tt_cross_reference(code)
+ return if in_tidylink_label?
+
+ crossref_regexp = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
+ # REGEXP sometimes matches a string that starts with a backslash but is not a
+ # suppressed cross-reference (for example, `\+`), so the backslash-removed
+ # part needs to be checked against crossref_regexp.
+ match = crossref_regexp.match(code.delete_prefix('\\'))
+ return unless match && match.begin(1).zero?
+ return unless match.post_match.match?(/\A[[:punct:]\s]*\z/)
+
+ # cross_reference(file_page) may return a link without code tag.
+ # We need to check it because this method shouldn't return an html text without code tag.
+ if code.start_with?('\\')
+ # Remove leading backslash if crossref exists
+ "#{convert_string(code[1..])}" if cross_reference(code[1..])&.include?('')
+ else
+ html = cross_reference(code)
+ html if html&.include?('')
+ end
+ end
end
end
end
diff --git a/lib/rdoc/markup/to_html_snippet.rb b/lib/rdoc/markup/to_html_snippet.rb
index 796c268539..3d184d6fff 100644
--- a/lib/rdoc/markup/to_html_snippet.rb
+++ b/lib/rdoc/markup/to_html_snippet.rb
@@ -1,287 +1,291 @@
# frozen_string_literal: true
-##
-# Outputs RDoc markup as paragraphs with inline markup only.
+module RDoc
+ class Markup
+ ##
+ # Outputs RDoc markup as paragraphs with inline markup only.
-class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
+ class ToHtmlSnippet < Markup::ToHtml
- ##
- # After this many characters the input will be cut off.
+ ##
+ # After this many characters the input will be cut off.
- attr_reader :character_limit
+ attr_reader :character_limit
- ##
- # The number of characters seen so far.
+ ##
+ # The number of characters seen so far.
- attr_reader :characters # :nodoc:
+ attr_reader :characters # :nodoc:
- ##
- # The attribute bitmask
+ ##
+ # The attribute bitmask
- attr_reader :mask
+ attr_reader :mask
- ##
- # After this many paragraphs the input will be cut off.
+ ##
+ # After this many paragraphs the input will be cut off.
- attr_reader :paragraph_limit
+ attr_reader :paragraph_limit
- ##
- # Count of paragraphs found
+ ##
+ # Count of paragraphs found
- attr_reader :paragraphs
+ attr_reader :paragraphs
- ##
- # Creates a new ToHtmlSnippet formatter that will cut off the input on the
- # next word boundary after the given number of +characters+ or +paragraphs+
- # of text have been encountered.
+ ##
+ # Creates a new ToHtmlSnippet formatter that will cut off the input on the
+ # next word boundary after the given number of +characters+ or +paragraphs+
+ # of text have been encountered.
- def initialize(characters = 100, paragraphs = 3)
- super()
+ def initialize(characters = 100, paragraphs = 3)
+ super()
- @character_limit = characters
- @paragraph_limit = paragraphs
+ @character_limit = characters
+ @paragraph_limit = paragraphs
- @characters = 0
- @mask = 0
- @paragraphs = 0
+ @characters = 0
+ @mask = 0
+ @paragraphs = 0
- @markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
- end
+ @markup.add_regexp_handling CrossReference::CROSSREF_REGEXP, :CROSSREF
+ end
- ##
- # Adds +heading+ to the output as a paragraph
+ ##
+ # Adds +heading+ to the output as a paragraph
- def accept_heading(heading)
- @res << "#{to_html heading.text}\n"
+ def accept_heading(heading)
+ @res << "
#{to_html heading.text}\n"
- add_paragraph
- end
+ add_paragraph
+ end
- ##
- # Raw sections are untrusted and ignored
+ ##
+ # Raw sections are untrusted and ignored
- alias accept_raw ignore
+ alias accept_raw ignore
- ##
- # Rules are ignored
+ ##
+ # Rules are ignored
- alias accept_rule ignore
+ alias accept_rule ignore
- ##
- # Adds +paragraph+ to the output
+ ##
+ # Adds +paragraph+ to the output
- def accept_paragraph(paragraph)
- para = @in_list_entry.last || "
"
+ def accept_paragraph(paragraph)
+ para = @in_list_entry.last || "
"
- text = paragraph.text @hard_break
+ text = paragraph.text @hard_break
- @res << "#{para}#{to_html text}\n"
+ @res << "#{para}#{to_html text}\n"
- add_paragraph
- end
+ add_paragraph
+ end
- ##
- # Finishes consumption of +list_item+
+ ##
+ # Finishes consumption of +list_item+
- def accept_list_item_end(list_item)
- end
+ def accept_list_item_end(list_item)
+ end
- ##
- # Prepares the visitor for consuming +list_item+
+ ##
+ # Prepares the visitor for consuming +list_item+
- def accept_list_item_start(list_item)
- @res << list_item_start(list_item, @list.last)
- end
+ def accept_list_item_start(list_item)
+ @res << list_item_start(list_item, @list.last)
+ end
- ##
- # Prepares the visitor for consuming +list+
+ ##
+ # Prepares the visitor for consuming +list+
- def accept_list_start(list)
- @list << list.type
- @res << html_list_name(list.type, true)
- @in_list_entry.push ''
- end
+ def accept_list_start(list)
+ @list << list.type
+ @res << html_list_name(list.type, true)
+ @in_list_entry.push ''
+ end
- ##
- # Adds +verbatim+ to the output
+ ##
+ # Adds +verbatim+ to the output
- def accept_verbatim(verbatim)
- throw :done if @characters >= @character_limit
- input = verbatim.text.rstrip
- text = truncate(input, @character_limit - @characters)
- @characters += input.length
- text << " #{TO_HTML_CHARACTERS[text.encoding][:ellipsis]}" unless text == input
+ def accept_verbatim(verbatim)
+ throw :done if @characters >= @character_limit
+ input = verbatim.text.rstrip
+ text = truncate(input, @character_limit - @characters)
+ @characters += input.length
+ text << " #{TO_HTML_CHARACTERS[text.encoding][:ellipsis]}" unless text == input
- super RDoc::Markup::Verbatim.new text
+ super Markup::Verbatim.new text
- add_paragraph
- end
+ add_paragraph
+ end
- ##
- # Prepares the visitor for HTML snippet generation
+ ##
+ # Prepares the visitor for HTML snippet generation
- def start_accepting
- super
+ def start_accepting
+ super
- @characters = 0
- end
+ @characters = 0
+ end
- ##
- # Removes escaping from the cross-references in +target+
+ ##
+ # Removes escaping from the cross-references in +target+
- def handle_regexp_CROSSREF(text)
- text.sub(/\A\\/, '')
- end
+ def handle_regexp_CROSSREF(text)
+ text.sub(/\A\\/, '')
+ end
- ##
- # Lists are paragraphs, but notes and labels have a separator
+ ##
+ # Lists are paragraphs, but notes and labels have a separator
- def list_item_start(list_item, list_type)
- throw :done if @characters >= @character_limit
+ def list_item_start(list_item, list_type)
+ throw :done if @characters >= @character_limit
- case list_type
- when :BULLET, :LALPHA, :NUMBER, :UALPHA
- "
"
- when :LABEL, :NOTE
- labels = Array(list_item.label).map do |label|
- to_html label
- end.join ', '
+ case list_type
+ when :BULLET, :LALPHA, :NUMBER, :UALPHA
+ "
"
+ when :LABEL, :NOTE
+ labels = Array(list_item.label).map do |label|
+ to_html label
+ end.join ', '
- labels << " — " unless labels.empty?
+ labels << " — " unless labels.empty?
- start = "
#{labels}"
- @characters += 1 # try to include the label
- start
- else
- raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
- end
- end
+ start = "
#{labels}"
+ @characters += 1 # try to include the label
+ start
+ else
+ raise Error, "Invalid list type: #{list_type.inspect}"
+ end
+ end
- ##
- # Returns just the text of +link+, +url+ is only used to determine the link
- # type.
-
- def gen_url(url, text)
- if url =~ /^rdoc-label:([^:]*)(?::(.*))?/
- type = "link"
- elsif url =~ /([A-Za-z]+):(.*)/
- type = $1
- else
- type = "http"
- end
+ ##
+ # Returns just the text of +link+, +url+ is only used to determine the link
+ # type.
- if (type == "http" or type == "https" or type == "link") and
- url =~ /\.(gif|png|jpg|jpeg|bmp)$/
- ''
- else
- text.sub(%r%^#{type}:/*%, '')
- end
- end
+ def gen_url(url, text)
+ if url =~ /^rdoc-label:([^:]*)(?::(.*))?/
+ type = "link"
+ elsif url =~ /([A-Za-z]+):(.*)/
+ type = $1
+ else
+ type = "http"
+ end
- ##
- # In snippets, there are no lists
+ if (type == "http" or type == "https" or type == "link") and
+ url =~ /\.(gif|png|jpg|jpeg|bmp)$/
+ ''
+ else
+ text.sub(%r%^#{type}:/*%, '')
+ end
+ end
+
+ ##
+ # In snippets, there are no lists
+
+ def html_list_name(list_type, open_tag)
+ ''
+ end
+
+ ##
+ # Throws +:done+ when paragraph_limit paragraphs have been encountered
+
+ def add_paragraph
+ @paragraphs += 1
+
+ throw :done if @paragraphs >= @paragraph_limit
+ end
+
+ ##
+ # Marks up +content+
+
+ def convert(content)
+ catch :done do
+ return super
+ end
+
+ end_accepting
+ end
+
+ def handle_PLAIN_TEXT(text) # :nodoc:
+ return if inline_limit_reached?
+
+ truncated = truncate(text, @inline_character_limit)
+ @inline_character_limit -= text.size
+ emit_inline(convert_string(truncated))
+ end
+
+ def handle_REGEXP_HANDLING_TEXT(text) # :nodoc:
+ return if inline_limit_reached?
+
+ # We can't truncate text including html tags.
+ # Just emit as is, and count all characters including html tag part.
+ emit_inline(text)
+ @inline_character_limit -= text.size
+ end
+
+ def handle_BOLD(nodes)
+ super unless inline_limit_reached?
+ end
+
+ def handle_BOLD_WORD(word)
+ super unless inline_limit_reached?
+ end
+
+ def handle_EM(nodes)
+ super unless inline_limit_reached?
+ end
+
+ def handle_EM_WORD(word)
+ super unless inline_limit_reached?
+ end
+
+ def handle_TT(code)
+ super unless inline_limit_reached?
+ end
+
+ def handle_STRIKE(nodes)
+ super unless inline_limit_reached?
+ end
+
+ def handle_HARD_BREAK
+ super unless inline_limit_reached?
+ end
+
+ def handle_TIDYLINK(label_part, url)
+ traverse_inline_nodes(label_part) unless inline_limit_reached?
+ end
+
+ def inline_limit_reached?
+ @inline_character_limit <= 0
+ end
+
+ def handle_inline(text)
+ limit = @character_limit - @characters
+ return ['', 0] if limit <= 0
+ @inline_character_limit = limit
+ res = super
+ res << " #{TO_HTML_CHARACTERS[text.encoding][:ellipsis]}" if @inline_character_limit <= 0
+ @characters += limit - @inline_character_limit
+ res
+ end
- def html_list_name(list_type, open_tag)
- ''
- end
+ def to_html(item)
+ throw :done if @characters >= @character_limit
+ handle_inline(item)
+ end
- ##
- # Throws +:done+ when paragraph_limit paragraphs have been encountered
+ ##
+ # Truncates +text+ at the end of the first word after the limit.
- def add_paragraph
- @paragraphs += 1
+ def truncate(text, limit)
+ return text if limit >= text.size
+ return '' if limit <= 0
- throw :done if @paragraphs >= @paragraph_limit
- end
+ text =~ /\A(.{#{limit},}?)(\s|$)/m # TODO word-break instead of \s?
- ##
- # Marks up +content+
+ $1
+ end
- def convert(content)
- catch :done do
- return super
end
-
- end_accepting
- end
-
- def handle_PLAIN_TEXT(text) # :nodoc:
- return if inline_limit_reached?
-
- truncated = truncate(text, @inline_character_limit)
- @inline_character_limit -= text.size
- emit_inline(convert_string(truncated))
end
-
- def handle_REGEXP_HANDLING_TEXT(text) # :nodoc:
- return if inline_limit_reached?
-
- # We can't truncate text including html tags.
- # Just emit as is, and count all characters including html tag part.
- emit_inline(text)
- @inline_character_limit -= text.size
- end
-
- def handle_BOLD(nodes)
- super unless inline_limit_reached?
- end
-
- def handle_BOLD_WORD(word)
- super unless inline_limit_reached?
- end
-
- def handle_EM(nodes)
- super unless inline_limit_reached?
- end
-
- def handle_EM_WORD(word)
- super unless inline_limit_reached?
- end
-
- def handle_TT(code)
- super unless inline_limit_reached?
- end
-
- def handle_STRIKE(nodes)
- super unless inline_limit_reached?
- end
-
- def handle_HARD_BREAK
- super unless inline_limit_reached?
- end
-
- def handle_TIDYLINK(label_part, url)
- traverse_inline_nodes(label_part) unless inline_limit_reached?
- end
-
- def inline_limit_reached?
- @inline_character_limit <= 0
- end
-
- def handle_inline(text)
- limit = @character_limit - @characters
- return ['', 0] if limit <= 0
- @inline_character_limit = limit
- res = super
- res << " #{TO_HTML_CHARACTERS[text.encoding][:ellipsis]}" if @inline_character_limit <= 0
- @characters += limit - @inline_character_limit
- res
- end
-
- def to_html(item)
- throw :done if @characters >= @character_limit
- handle_inline(item)
- end
-
- ##
- # Truncates +text+ at the end of the first word after the limit.
-
- def truncate(text, limit)
- return text if limit >= text.size
- return '' if limit <= 0
-
- text =~ /\A(.{#{limit},}?)(\s|$)/m # TODO word-break instead of \s?
-
- $1
- end
-
end
diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb
index 27060e226c..4ed73b1306 100644
--- a/lib/rdoc/markup/to_joined_paragraph.rb
+++ b/lib/rdoc/markup/to_joined_paragraph.rb
@@ -1,41 +1,45 @@
# frozen_string_literal: true
-##
-# Joins the parts of an RDoc::Markup::Paragraph into a single String.
-#
-# This allows for easier maintenance and testing of Markdown support.
-#
-# This formatter only works on Paragraph instances. Attempting to process
-# other markup syntax items will not work.
+module RDoc
+ class Markup
+ ##
+ # Joins the parts of an RDoc::Markup::Paragraph into a single String.
+ #
+ # This allows for easier maintenance and testing of Markdown support.
+ #
+ # This formatter only works on Paragraph instances. Attempting to process
+ # other markup syntax items will not work.
-class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
- def start_accepting # :nodoc:
- end
+ class ToJoinedParagraph < Markup::Formatter
+ def start_accepting # :nodoc:
+ end
- def end_accepting # :nodoc:
- end
+ def end_accepting # :nodoc:
+ end
- ##
- # Converts the parts of +paragraph+ to a single entry.
+ ##
+ # Converts the parts of +paragraph+ to a single entry.
- def accept_paragraph(paragraph)
- parts = paragraph.parts.chunk do |part|
- String === part
- end.flat_map do |string, chunk|
- string ? chunk.join.rstrip : chunk
- end
+ def accept_paragraph(paragraph)
+ parts = paragraph.parts.chunk do |part|
+ String === part
+ end.flat_map do |string, chunk|
+ string ? chunk.join.rstrip : chunk
+ end
- paragraph.parts.replace parts
- end
+ paragraph.parts.replace parts
+ end
- alias accept_block_quote ignore
- alias accept_heading ignore
- alias accept_list_end ignore
- alias accept_list_item_end ignore
- alias accept_list_item_start ignore
- alias accept_list_start ignore
- alias accept_raw ignore
- alias accept_rule ignore
- alias accept_verbatim ignore
- alias accept_table ignore
+ alias accept_block_quote ignore
+ alias accept_heading ignore
+ alias accept_list_end ignore
+ alias accept_list_item_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_start ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_verbatim ignore
+ alias accept_table ignore
+ end
+ end
end
diff --git a/lib/rdoc/markup/to_label.rb b/lib/rdoc/markup/to_label.rb
index ae11a5fe6b..20166c6ec4 100644
--- a/lib/rdoc/markup/to_label.rb
+++ b/lib/rdoc/markup/to_label.rb
@@ -2,83 +2,87 @@
require 'cgi/escape'
require 'cgi/util' unless defined?(CGI::EscapeExt)
-##
-# Creates HTML-safe labels suitable for use in id attributes. Tidylinks are
-# converted to their link part and cross-reference links have the suppression
-# marks removed (\\SomeClass is converted to SomeClass).
+module RDoc
+ class Markup
+ ##
+ # Creates HTML-safe labels suitable for use in id attributes. Tidylinks are
+ # converted to their link part and cross-reference links have the suppression
+ # marks removed (\\SomeClass is converted to SomeClass).
-class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
+ class ToLabel < Markup::Formatter
- attr_reader :res # :nodoc:
+ attr_reader :res # :nodoc:
- ##
- # Creates a new formatter that will output HTML-safe labels
+ ##
+ # Creates a new formatter that will output HTML-safe labels
- def initialize
- super
+ def initialize
+ super
- @markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ @markup.add_regexp_handling CrossReference::CROSSREF_REGEXP, :CROSSREF
- @res = []
- end
+ @res = []
+ end
- def handle_PLAIN_TEXT(text)
- @res << text
- end
+ def handle_PLAIN_TEXT(text)
+ @res << text
+ end
- def handle_REGEXP_HANDLING_TEXT(text)
- @res << text
- end
+ def handle_REGEXP_HANDLING_TEXT(text)
+ @res << text
+ end
- def handle_TT(text)
- @res << text
- end
+ def handle_TT(text)
+ @res << text
+ end
- def extract_plaintext(text)
- @res = []
- handle_inline(text)
- @res.join
- end
+ def extract_plaintext(text)
+ @res = []
+ handle_inline(text)
+ @res.join
+ end
- ##
- # Converts +text+ to an HTML-safe label using GitHub-style anchor formatting.
+ ##
+ # Converts +text+ to an HTML-safe label using GitHub-style anchor formatting.
- def convert(text)
- label = extract_plaintext(text)
+ def convert(text)
+ label = extract_plaintext(text)
- RDoc::Text.to_anchor(label)
- end
+ Text.to_anchor(label)
+ end
- ##
- # Converts +text+ to an HTML-safe label using legacy RDoc formatting.
- # Used for generating backward-compatible anchor aliases.
+ ##
+ # Converts +text+ to an HTML-safe label using legacy RDoc formatting.
+ # Used for generating backward-compatible anchor aliases.
- def convert_legacy(text)
- label = extract_plaintext(text)
+ def convert_legacy(text)
+ label = extract_plaintext(text)
- CGI.escape(label).gsub('%', '-').sub(/^-/, '')
- end
+ CGI.escape(label).gsub('%', '-').sub(/^-/, '')
+ end
- ##
- # Converts the CROSSREF +target+ to plain text, removing the suppression
- # marker, if any
+ ##
+ # Converts the CROSSREF +target+ to plain text, removing the suppression
+ # marker, if any
- def handle_regexp_CROSSREF(text)
- text.sub(/^\\/, '')
- end
+ def handle_regexp_CROSSREF(text)
+ text.sub(/^\\/, '')
+ end
- alias accept_blank_line ignore
- alias accept_block_quote ignore
- alias accept_heading ignore
- alias accept_list_end ignore
- alias accept_list_item_end ignore
- alias accept_list_item_start ignore
- alias accept_list_start ignore
- alias accept_paragraph ignore
- alias accept_raw ignore
- alias accept_rule ignore
- alias accept_verbatim ignore
- alias end_accepting ignore
- alias start_accepting ignore
+ alias accept_blank_line ignore
+ alias accept_block_quote ignore
+ alias accept_heading ignore
+ alias accept_list_end ignore
+ alias accept_list_item_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_start ignore
+ alias accept_paragraph ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_verbatim ignore
+ alias end_accepting ignore
+ alias start_accepting ignore
+ end
+ end
end
diff --git a/lib/rdoc/markup/to_markdown.rb b/lib/rdoc/markup/to_markdown.rb
index 18e1c86f52..24a0a2f4ac 100644
--- a/lib/rdoc/markup/to_markdown.rb
+++ b/lib/rdoc/markup/to_markdown.rb
@@ -1,215 +1,219 @@
# frozen_string_literal: true
# :markup: markdown
-##
-# Outputs parsed markup as Markdown
+module RDoc
+ class Markup
+ ##
+ # Outputs parsed markup as Markdown
+
+ class ToMarkdown < Markup::ToRdoc
+
+ ##
+ # Creates a new formatter that will output Markdown format text
+
+ def initialize
+ super
+
+ @headings[1] = ['# ', '']
+ @headings[2] = ['## ', '']
+ @headings[3] = ['### ', '']
+ @headings[4] = ['#### ', '']
+ @headings[5] = ['##### ', '']
+ @headings[6] = ['###### ', '']
+
+ add_regexp_handling_RDOCLINK
+
+ @hard_break = " \n"
+ end
+
+ ##
+ # Finishes consumption of `list`
+
+ def accept_list_end(list)
+ super
+ end
+
+ ##
+ # Finishes consumption of `list_item`
+
+ def accept_list_item_end(list_item)
+ width = case @list_type.last
+ when :BULLET
+ 4
+ when :NOTE, :LABEL
+ use_prefix
+
+ @res << "\n"
+
+ 4
+ else
+ @list_index[-1] = @list_index.last.succ
+ 4
+ end
+
+ @indent -= width
+ end
+
+ ##
+ # Prepares the visitor for consuming `list_item`
+
+ def accept_list_item_start(list_item)
+ type = @list_type.last
+
+ case type
+ when :NOTE, :LABEL
+ bullets = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ bullets << "\n" unless bullets.empty?
+
+ @prefix = ' ' * @indent
+ @indent += 4
+ @prefix << bullets << ":" << (' ' * (@indent - 1))
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(4)
+
+ @indent += 4
+ end
+ end
+
+ def add_tag(tag, simple_tag, content)
+ if content.match?(/\A[\w\s]+\z/)
+ emit_inline("#{simple_tag}#{content}#{simple_tag}")
+ else
+ emit_inline("<#{tag}>#{content}#{tag}>")
+ end
+ end
+
+ def handle_tag(nodes, simple_tag, tag)
+ if nodes.size == 1 && String === nodes[0]
+ content = apply_regexp_handling(nodes[0]).map do |text, converted|
+ converted ? text : convert_string(text)
+ end.join
+ add_tag(tag, simple_tag, content)
+ else
+ emit_inline("<#{tag}>")
+ traverse_inline_nodes(nodes)
+ emit_inline("#{tag}>")
+ end
+ end
+
+ def handle_TIDYLINK(label_part, url)
+ if url =~ /^rdoc-label:foot/
+ emit_inline(handle_rdoc_link(url))
+ else
+ emit_inline('[')
+ traverse_inline_nodes(label_part)
+ emit_inline("](#{url})")
+ end
+ end
+
+ def handle_BOLD(nodes)
+ handle_tag(nodes, '**', 'strong')
+ end
+
+ def handle_EM(nodes)
+ handle_tag(nodes, '*', 'em')
+ end
+
+ def handle_BOLD_WORD(word)
+ add_tag('strong', '**', convert_string(word))
+ end
+
+ def handle_EM_WORD(word)
+ add_tag('em', '*', convert_string(word))
+ end
+
+ def handle_TT(text)
+ add_tag('code', '`', convert_string(text))
+ end
+
+ def handle_STRIKE(nodes)
+ handle_tag(nodes, '~~', 's')
+ end
+
+ def handle_HARD_BREAK
+ emit_inline(" \n")
+ end
+
+ ##
+ # Prepares the visitor for consuming `list`
+
+ def accept_list_start(list)
+ case list.type
+ when :BULLET, :LABEL, :NOTE
+ @list_index << nil
+ when :LALPHA, :NUMBER, :UALPHA
+ @list_index << 1
+ else
+ raise Error, "invalid list type #{list.type}"
+ end
+
+ @list_width << 4
+ @list_type << list.type
+ end
+
+ ##
+ # Adds `rule` to the output
+
+ def accept_rule(rule)
+ use_prefix or @res << ' ' * @indent
+ @res << '-' * 3
+ @res << "\n"
+ end
+
+ ##
+ # Outputs `verbatim` indented 4 columns
+
+ def accept_verbatim(verbatim)
+ indent = ' ' * (@indent + 4)
+
+ verbatim.parts.each do |part|
+ @res << indent unless part == "\n"
+ @res << part
+ end
+
+ @res << "\n"
+ end
+
+ ##
+ # Creates a Markdown-style URL from +url+ with +text+.
+
+ def gen_url(url, text)
+ scheme, url, = parse_url url
+
+ "[#{text.sub(%r{^#{scheme}:/*}i, '')}](#{url})"
+ end
+
+ ##
+ # Handles rdoc- type links for footnotes.
+
+ def handle_rdoc_link(url)
+ case url
+ when /^rdoc-ref:/
+ $'
+ when /^rdoc-label:footmark-(\d+)/
+ "[^#{$1}]:"
+ when /^rdoc-label:foottext-(\d+)/
+ "[^#{$1}]"
+ when /^rdoc-label:label-/
+ gen_url url, $'
+ when /^rdoc-image:/
+ ""
+ when /^rdoc-[a-z]+:/
+ $'
+ end
+ end
+
+ ##
+ # Converts the rdoc-...: links into a Markdown.style links.
+
+ def handle_regexp_RDOCLINK(text)
+ handle_rdoc_link text
+ end
-class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
-
- ##
- # Creates a new formatter that will output Markdown format text
-
- def initialize
- super
-
- @headings[1] = ['# ', '']
- @headings[2] = ['## ', '']
- @headings[3] = ['### ', '']
- @headings[4] = ['#### ', '']
- @headings[5] = ['##### ', '']
- @headings[6] = ['###### ', '']
-
- add_regexp_handling_RDOCLINK
-
- @hard_break = " \n"
- end
-
- ##
- # Finishes consumption of `list`
-
- def accept_list_end(list)
- super
- end
-
- ##
- # Finishes consumption of `list_item`
-
- def accept_list_item_end(list_item)
- width = case @list_type.last
- when :BULLET
- 4
- when :NOTE, :LABEL
- use_prefix
-
- @res << "\n"
-
- 4
- else
- @list_index[-1] = @list_index.last.succ
- 4
- end
-
- @indent -= width
- end
-
- ##
- # Prepares the visitor for consuming `list_item`
-
- def accept_list_item_start(list_item)
- type = @list_type.last
-
- case type
- when :NOTE, :LABEL
- bullets = Array(list_item.label).map do |label|
- attributes(label).strip
- end.join "\n"
-
- bullets << "\n" unless bullets.empty?
-
- @prefix = ' ' * @indent
- @indent += 4
- @prefix << bullets << ":" << (' ' * (@indent - 1))
- else
- bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
- @prefix = (' ' * @indent) + bullet.ljust(4)
-
- @indent += 4
- end
- end
-
- def add_tag(tag, simple_tag, content)
- if content.match?(/\A[\w\s]+\z/)
- emit_inline("#{simple_tag}#{content}#{simple_tag}")
- else
- emit_inline("<#{tag}>#{content}#{tag}>")
- end
- end
-
- def handle_tag(nodes, simple_tag, tag)
- if nodes.size == 1 && String === nodes[0]
- content = apply_regexp_handling(nodes[0]).map do |text, converted|
- converted ? text : convert_string(text)
- end.join
- add_tag(tag, simple_tag, content)
- else
- emit_inline("<#{tag}>")
- traverse_inline_nodes(nodes)
- emit_inline("#{tag}>")
- end
- end
-
- def handle_TIDYLINK(label_part, url)
- if url =~ /^rdoc-label:foot/
- emit_inline(handle_rdoc_link(url))
- else
- emit_inline('[')
- traverse_inline_nodes(label_part)
- emit_inline("](#{url})")
- end
- end
-
- def handle_BOLD(nodes)
- handle_tag(nodes, '**', 'strong')
- end
-
- def handle_EM(nodes)
- handle_tag(nodes, '*', 'em')
- end
-
- def handle_BOLD_WORD(word)
- add_tag('strong', '**', convert_string(word))
- end
-
- def handle_EM_WORD(word)
- add_tag('em', '*', convert_string(word))
- end
-
- def handle_TT(text)
- add_tag('code', '`', convert_string(text))
- end
-
- def handle_STRIKE(nodes)
- handle_tag(nodes, '~~', 's')
- end
-
- def handle_HARD_BREAK
- emit_inline(" \n")
- end
-
- ##
- # Prepares the visitor for consuming `list`
-
- def accept_list_start(list)
- case list.type
- when :BULLET, :LABEL, :NOTE
- @list_index << nil
- when :LALPHA, :NUMBER, :UALPHA
- @list_index << 1
- else
- raise RDoc::Error, "invalid list type #{list.type}"
- end
-
- @list_width << 4
- @list_type << list.type
- end
-
- ##
- # Adds `rule` to the output
-
- def accept_rule(rule)
- use_prefix or @res << ' ' * @indent
- @res << '-' * 3
- @res << "\n"
- end
-
- ##
- # Outputs `verbatim` indented 4 columns
-
- def accept_verbatim(verbatim)
- indent = ' ' * (@indent + 4)
-
- verbatim.parts.each do |part|
- @res << indent unless part == "\n"
- @res << part
end
-
- @res << "\n"
end
-
- ##
- # Creates a Markdown-style URL from +url+ with +text+.
-
- def gen_url(url, text)
- scheme, url, = parse_url url
-
- "[#{text.sub(%r{^#{scheme}:/*}i, '')}](#{url})"
- end
-
- ##
- # Handles rdoc- type links for footnotes.
-
- def handle_rdoc_link(url)
- case url
- when /^rdoc-ref:/
- $'
- when /^rdoc-label:footmark-(\d+)/
- "[^#{$1}]:"
- when /^rdoc-label:foottext-(\d+)/
- "[^#{$1}]"
- when /^rdoc-label:label-/
- gen_url url, $'
- when /^rdoc-image:/
- ""
- when /^rdoc-[a-z]+:/
- $'
- end
- end
-
- ##
- # Converts the rdoc-...: links into a Markdown.style links.
-
- def handle_regexp_RDOCLINK(text)
- handle_rdoc_link text
- end
-
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 4001760426..e2428fdda2 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -1,421 +1,425 @@
# frozen_string_literal: true
-##
-# Outputs RDoc markup as RDoc markup! (mostly)
+module RDoc
+ class Markup
+ ##
+ # Outputs RDoc markup as RDoc markup! (mostly)
-class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
- DEFAULT_HEADINGS = {
- 1 => ['= ', ''],
- 2 => ['== ', ''],
- 3 => ['=== ', ''],
- 4 => ['==== ', ''],
- 5 => ['===== ', ''],
- 6 => ['====== ', '']
- }
- DEFAULT_HEADINGS.default = []
- DEFAULT_HEADINGS.freeze
+ class ToRdoc < Markup::Formatter
+ DEFAULT_HEADINGS = {
+ 1 => ['= ', ''],
+ 2 => ['== ', ''],
+ 3 => ['=== ', ''],
+ 4 => ['==== ', ''],
+ 5 => ['===== ', ''],
+ 6 => ['====== ', '']
+ }
+ DEFAULT_HEADINGS.default = []
+ DEFAULT_HEADINGS.freeze
- ##
- # Current indent amount for output in characters
+ ##
+ # Current indent amount for output in characters
- attr_accessor :indent
+ attr_accessor :indent
- ##
- # Output width in characters
+ ##
+ # Output width in characters
- attr_accessor :width
+ attr_accessor :width
- ##
- # Stack of current list indexes for alphabetic and numeric lists
+ ##
+ # Stack of current list indexes for alphabetic and numeric lists
- attr_reader :list_index
+ attr_reader :list_index
- ##
- # Stack of list types
+ ##
+ # Stack of list types
- attr_reader :list_type
+ attr_reader :list_type
- ##
- # Stack of list widths for indentation
+ ##
+ # Stack of list widths for indentation
- attr_reader :list_width
+ attr_reader :list_width
- ##
- # Prefix for the next list item. See #use_prefix
+ ##
+ # Prefix for the next list item. See #use_prefix
- attr_reader :prefix
+ attr_reader :prefix
- ##
- # Output accumulator
+ ##
+ # Output accumulator
- attr_reader :res
+ attr_reader :res
- ##
- # Creates a new formatter that will output (mostly) \RDoc markup
+ ##
+ # Creates a new formatter that will output (mostly) \RDoc markup
- def initialize
- super
+ def initialize
+ super
- @markup.add_regexp_handling(/\\\S/, :SUPPRESSED_CROSSREF)
- @width = 78
+ @markup.add_regexp_handling(/\\\S/, :SUPPRESSED_CROSSREF)
+ @width = 78
- @headings = DEFAULT_HEADINGS.dup
- @hard_break = "\n"
- end
-
- ##
- # Adds +blank_line+ to the output
-
- def accept_blank_line(blank_line)
- @res << "\n"
- end
-
- ##
- # Adds +paragraph+ to the output
-
- def accept_block_quote(block_quote)
- @indent += 2
+ @headings = DEFAULT_HEADINGS.dup
+ @hard_break = "\n"
+ end
- block_quote.parts.each do |part|
- @prefix = '> '
+ ##
+ # Adds +blank_line+ to the output
- part.accept self
- end
+ def accept_blank_line(blank_line)
+ @res << "\n"
+ end
- @indent -= 2
- end
+ ##
+ # Adds +paragraph+ to the output
- ##
- # Adds +heading+ to the output
+ def accept_block_quote(block_quote)
+ @indent += 2
- def accept_heading(heading)
- use_prefix or @res << ' ' * @indent
- @res << @headings[heading.level][0]
- @res << attributes(heading.text)
- @res << @headings[heading.level][1]
- @res << "\n"
- end
+ block_quote.parts.each do |part|
+ @prefix = '> '
- ##
- # Finishes consumption of +list+
+ part.accept self
+ end
- def accept_list_end(list)
- @list_index.pop
- @list_type.pop
- @list_width.pop
- end
+ @indent -= 2
+ end
- ##
- # Finishes consumption of +list_item+
-
- def accept_list_item_end(list_item)
- width = case @list_type.last
- when :BULLET
- 2
- when :NOTE, :LABEL
- if @prefix
- @res << @prefix.strip
- @prefix = nil
- end
-
- @res << "\n"
- 2
- else
- bullet = @list_index.last.to_s
- @list_index[-1] = @list_index.last.succ
- bullet.length + 2
- end
-
- @indent -= width
- end
+ ##
+ # Adds +heading+ to the output
- ##
- # Prepares the visitor for consuming +list_item+
+ def accept_heading(heading)
+ use_prefix or @res << ' ' * @indent
+ @res << @headings[heading.level][0]
+ @res << attributes(heading.text)
+ @res << @headings[heading.level][1]
+ @res << "\n"
+ end
- def accept_list_item_start(list_item)
- type = @list_type.last
+ ##
+ # Finishes consumption of +list+
- case type
- when :NOTE, :LABEL
- stripped_labels = Array(list_item.label).map do |label|
- attributes(label).strip
+ def accept_list_end(list)
+ @list_index.pop
+ @list_type.pop
+ @list_width.pop
end
- bullets = case type
- when :NOTE
- stripped_labels.map { |b| "#{b}::" }
- when :LABEL
- stripped_labels.map { |b| "[#{b}]" }
+ ##
+ # Finishes consumption of +list_item+
+
+ def accept_list_item_end(list_item)
+ width = case @list_type.last
+ when :BULLET
+ 2
+ when :NOTE, :LABEL
+ if @prefix
+ @res << @prefix.strip
+ @prefix = nil
+ end
+
+ @res << "\n"
+ 2
+ else
+ bullet = @list_index.last.to_s
+ @list_index[-1] = @list_index.last.succ
+ bullet.length + 2
+ end
+
+ @indent -= width
end
- bullets = bullets.join("\n")
- bullets << "\n" unless stripped_labels.empty?
-
- @prefix = ' ' * @indent
- @indent += 2
- @prefix << bullets + (' ' * @indent)
- else
- bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
- @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
- width = bullet.length + 1
- @indent += width
- end
- end
-
- ##
- # Prepares the visitor for consuming +list+
-
- def accept_list_start(list)
- case list.type
- when :BULLET
- @list_index << nil
- @list_width << 1
- when :LABEL, :NOTE
- @list_index << nil
- @list_width << 2
- when :LALPHA
- @list_index << 'a'
- @list_width << list.items.length.to_s.length
- when :NUMBER
- @list_index << 1
- @list_width << list.items.length.to_s.length
- when :UALPHA
- @list_index << 'A'
- @list_width << list.items.length.to_s.length
- else
- raise RDoc::Error, "invalid list type #{list.type}"
- end
+ ##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start(list_item)
+ type = @list_type.last
+
+ case type
+ when :NOTE, :LABEL
+ stripped_labels = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end
+
+ bullets = case type
+ when :NOTE
+ stripped_labels.map { |b| "#{b}::" }
+ when :LABEL
+ stripped_labels.map { |b| "[#{b}]" }
+ end
+
+ bullets = bullets.join("\n")
+ bullets << "\n" unless stripped_labels.empty?
+
+ @prefix = ' ' * @indent
+ @indent += 2
+ @prefix << bullets + (' ' * @indent)
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+ width = bullet.length + 1
+ @indent += width
+ end
+ end
- @list_type << list.type
- end
+ ##
+ # Prepares the visitor for consuming +list+
+
+ def accept_list_start(list)
+ case list.type
+ when :BULLET
+ @list_index << nil
+ @list_width << 1
+ when :LABEL, :NOTE
+ @list_index << nil
+ @list_width << 2
+ when :LALPHA
+ @list_index << 'a'
+ @list_width << list.items.length.to_s.length
+ when :NUMBER
+ @list_index << 1
+ @list_width << list.items.length.to_s.length
+ when :UALPHA
+ @list_index << 'A'
+ @list_width << list.items.length.to_s.length
+ else
+ raise Error, "invalid list type #{list.type}"
+ end
+
+ @list_type << list.type
+ end
- ##
- # Adds +paragraph+ to the output
+ ##
+ # Adds +paragraph+ to the output
- def accept_paragraph(paragraph)
- text = paragraph.text @hard_break
- wrap attributes text
- end
+ def accept_paragraph(paragraph)
+ text = paragraph.text @hard_break
+ wrap attributes text
+ end
- ##
- # Adds +paragraph+ to the output
+ ##
+ # Adds +paragraph+ to the output
- def accept_indented_paragraph(paragraph)
- @indent += paragraph.indent
- text = paragraph.text @hard_break
- wrap attributes text
- @indent -= paragraph.indent
- end
+ def accept_indented_paragraph(paragraph)
+ @indent += paragraph.indent
+ text = paragraph.text @hard_break
+ wrap attributes text
+ @indent -= paragraph.indent
+ end
- ##
- # Adds +raw+ to the output
+ ##
+ # Adds +raw+ to the output
- def accept_raw(raw)
- @res << raw.parts.join("\n")
- end
+ def accept_raw(raw)
+ @res << raw.parts.join("\n")
+ end
- ##
- # Adds +rule+ to the output
+ ##
+ # Adds +rule+ to the output
- def accept_rule(rule)
- use_prefix or @res << ' ' * @indent
- @res << '-' * (@width - @indent)
- @res << "\n"
- end
+ def accept_rule(rule)
+ use_prefix or @res << ' ' * @indent
+ @res << '-' * (@width - @indent)
+ @res << "\n"
+ end
- ##
- # Outputs +verbatim+ indented 2 columns
+ ##
+ # Outputs +verbatim+ indented 2 columns
- def accept_verbatim(verbatim)
- indent = ' ' * (@indent + 2)
+ def accept_verbatim(verbatim)
+ indent = ' ' * (@indent + 2)
- verbatim.parts.each do |part|
- @res << indent unless part == "\n"
- @res << part
- end
+ verbatim.parts.each do |part|
+ @res << indent unless part == "\n"
+ @res << part
+ end
- @res << "\n"
- end
+ @res << "\n"
+ end
- ##
- # Adds +table+ to the output
+ ##
+ # Adds +table+ to the output
+
+ def accept_table(header, body, aligns)
+ header = header.map { |h| attributes h }
+ body = body.map { |row| row.map { |t| attributes t } }
+ widths = header.zip(*body).map do |cols|
+ cols.compact.map { |col| calculate_text_width(col) }.max
+ end
+ aligns = aligns.map do |a|
+ case a
+ when nil, :center
+ :center
+ when :left
+ :ljust
+ when :right
+ :rjust
+ end
+ end
+ @res << header.zip(widths, aligns).map do |h, w, a|
+ extra_width = h.size - calculate_text_width(h)
+ h.__send__(a, w + extra_width)
+ end.join("|").rstrip << "\n"
+ @res << widths.map {|w| "-" * w }.join("|") << "\n"
+ body.each do |row|
+ @res << widths.zip(aligns).each_with_index.map do |(w, a), i|
+ t = row[i] || ""
+ extra_width = t.size - calculate_text_width(t)
+ t.__send__(a, w + extra_width)
+ end.join("|").rstrip << "\n"
+ end
+ end
- def accept_table(header, body, aligns)
- header = header.map { |h| attributes h }
- body = body.map { |row| row.map { |t| attributes t } }
- widths = header.zip(*body).map do |cols|
- cols.compact.map { |col| calculate_text_width(col) }.max
- end
- aligns = aligns.map do |a|
- case a
- when nil, :center
- :center
- when :left
- :ljust
- when :right
- :rjust
+ def calculate_text_width(text)
+ text.size
end
- end
- @res << header.zip(widths, aligns).map do |h, w, a|
- extra_width = h.size - calculate_text_width(h)
- h.__send__(a, w + extra_width)
- end.join("|").rstrip << "\n"
- @res << widths.map {|w| "-" * w }.join("|") << "\n"
- body.each do |row|
- @res << widths.zip(aligns).each_with_index.map do |(w, a), i|
- t = row[i] || ""
- extra_width = t.size - calculate_text_width(t)
- t.__send__(a, w + extra_width)
- end.join("|").rstrip << "\n"
- end
- end
- def calculate_text_width(text)
- text.size
- end
+ def handle_PLAIN_TEXT(text)
+ add_text(text)
+ end
- def handle_PLAIN_TEXT(text)
- add_text(text)
- end
+ def handle_REGEXP_HANDLING_TEXT(text)
+ add_text(text)
+ end
- def handle_REGEXP_HANDLING_TEXT(text)
- add_text(text)
- end
+ def handle_BOLD(target)
+ on(:BOLD)
+ super
+ off(:BOLD)
+ end
- def handle_BOLD(target)
- on(:BOLD)
- super
- off(:BOLD)
- end
+ def handle_EM(target)
+ on(:EM)
+ super
+ off(:EM)
+ end
- def handle_EM(target)
- on(:EM)
- super
- off(:EM)
- end
+ def handle_BOLD_WORD(word)
+ on(:BOLD)
+ super
+ off(:BOLD)
+ end
- def handle_BOLD_WORD(word)
- on(:BOLD)
- super
- off(:BOLD)
- end
+ def handle_EM_WORD(word)
+ on(:EM)
+ super
+ off(:EM)
+ end
- def handle_EM_WORD(word)
- on(:EM)
- super
- off(:EM)
- end
+ def handle_TT(code)
+ on(:TT)
+ super
+ off(:TT)
+ end
- def handle_TT(code)
- on(:TT)
- super
- off(:TT)
- end
+ def handle_STRIKE(target)
+ on(:STRIKE)
+ super
+ off(:STRIKE)
+ end
- def handle_STRIKE(target)
- on(:STRIKE)
- super
- off(:STRIKE)
- end
+ def handle_HARD_BREAK
+ add_text("\n")
+ end
- def handle_HARD_BREAK
- add_text("\n")
- end
+ def handle_TIDYLINK(label_part, url)
+ super
+ add_text("( #{url} )")
+ end
- def handle_TIDYLINK(label_part, url)
- super
- add_text("( #{url} )")
- end
+ def handle_inline(text, initial_attributes = [])
+ @attributes = Hash.new(0)
+ initial_attributes.each { |attr| on(attr) }
+ out = @inline_output = +''
+ super(text)
+ @inline_output = nil
+ out
+ end
- def handle_inline(text, initial_attributes = [])
- @attributes = Hash.new(0)
- initial_attributes.each { |attr| on(attr) }
- out = @inline_output = +''
- super(text)
- @inline_output = nil
- out
- end
+ def on(attr)
+ @attributes[attr] += 1
+ end
- def on(attr)
- @attributes[attr] += 1
- end
+ def off(attr)
+ @attributes[attr] -= 1
+ @attributes.delete(attr) if @attributes[attr] == 0
+ end
- def off(attr)
- @attributes[attr] -= 1
- @attributes.delete(attr) if @attributes[attr] == 0
- end
+ def add_text(text)
+ emit_inline(text)
+ end
- def add_text(text)
- emit_inline(text)
- end
+ def emit_inline(text)
+ @inline_output << text
+ end
- def emit_inline(text)
- @inline_output << text
- end
+ ##
+ # Applies attribute-specific markup to +text+ using RDoc::Markup::InlineParser
- ##
- # Applies attribute-specific markup to +text+ using RDoc::Markup::InlineParser
+ def attributes(text)
+ handle_inline(text)
+ end
- def attributes(text)
- handle_inline(text)
- end
+ ##
+ # Returns the generated output
- ##
- # Returns the generated output
+ def end_accepting
+ @res.join
+ end
- def end_accepting
- @res.join
- end
+ ##
+ # Removes preceding \\ from the suppressed crossref +target+
- ##
- # Removes preceding \\ from the suppressed crossref +target+
+ def handle_regexp_SUPPRESSED_CROSSREF(text)
+ text.sub('\\', '')
+ end
- def handle_regexp_SUPPRESSED_CROSSREF(text)
- text.sub('\\', '')
- end
+ ##
+ # Prepares the visitor for text generation
- ##
- # Prepares the visitor for text generation
+ def start_accepting
+ @res = [""]
+ @indent = 0
+ @prefix = nil
- def start_accepting
- @res = [""]
- @indent = 0
- @prefix = nil
+ @list_index = []
+ @list_type = []
+ @list_width = []
+ end
- @list_index = []
- @list_type = []
- @list_width = []
- end
+ ##
+ # Adds the stored #prefix to the output and clears it. Lists generate a
+ # prefix for later consumption.
- ##
- # Adds the stored #prefix to the output and clears it. Lists generate a
- # prefix for later consumption.
+ def use_prefix
+ prefix, @prefix = @prefix, nil
+ @res << prefix if prefix
- def use_prefix
- prefix, @prefix = @prefix, nil
- @res << prefix if prefix
+ prefix
+ end
- prefix
- end
+ ##
+ # Wraps +text+ to #width
- ##
- # Wraps +text+ to #width
+ def wrap(text)
+ return unless text && !text.empty?
- def wrap(text)
- return unless text && !text.empty?
+ text_len = @width - @indent
- text_len = @width - @indent
+ text_len = 20 if text_len < 20
- text_len = 20 if text_len < 20
+ next_prefix = ' ' * @indent
- next_prefix = ' ' * @indent
+ prefix = @prefix || next_prefix
+ @prefix = nil
- prefix = @prefix || next_prefix
- @prefix = nil
+ text.scan(/\G(?:([^ \n]{#{text_len}})(?=[^ \n])|(.{1,#{text_len}})(?:[ \n]|\z))/) do
+ @res << prefix << ($1 || $2) << "\n"
+ prefix = next_prefix
+ end
+ end
- text.scan(/\G(?:([^ \n]{#{text_len}})(?=[^ \n])|(.{1,#{text_len}})(?:[ \n]|\z))/) do
- @res << prefix << ($1 || $2) << "\n"
- prefix = next_prefix
end
end
-
end
diff --git a/lib/rdoc/markup/to_table_of_contents.rb b/lib/rdoc/markup/to_table_of_contents.rb
index 31c849e7d6..44079f0189 100644
--- a/lib/rdoc/markup/to_table_of_contents.rb
+++ b/lib/rdoc/markup/to_table_of_contents.rb
@@ -1,88 +1,92 @@
# frozen_string_literal: true
-##
-# Extracts just the RDoc::Markup::Heading elements from a
-# RDoc::Markup::Document to help build a table of contents
+module RDoc
+ class Markup
+ ##
+ # Extracts just the RDoc::Markup::Heading elements from a
+ # RDoc::Markup::Document to help build a table of contents
-class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter
+ class ToTableOfContents < Markup::Formatter
- @to_toc = nil
+ @to_toc = nil
- ##
- # Singleton for table-of-contents generation
+ ##
+ # Singleton for table-of-contents generation
- def self.to_toc
- @to_toc ||= new
- end
+ def self.to_toc
+ @to_toc ||= new
+ end
- ##
- # Output accumulator
+ ##
+ # Output accumulator
- attr_reader :res
+ attr_reader :res
- ##
- # Omits headings with a level less than the given level.
+ ##
+ # Omits headings with a level less than the given level.
- attr_accessor :omit_headings_below
+ attr_accessor :omit_headings_below
- def initialize # :nodoc:
- super
+ def initialize # :nodoc:
+ super
- @omit_headings_below = nil
- end
+ @omit_headings_below = nil
+ end
- ##
- # Adds +document+ to the output, using its heading cutoff if present
+ ##
+ # Adds +document+ to the output, using its heading cutoff if present
- def accept_document(document)
- @omit_headings_below = document.omit_headings_below
+ def accept_document(document)
+ @omit_headings_below = document.omit_headings_below
- super
- end
+ super
+ end
- ##
- # Adds +heading+ to the table of contents
+ ##
+ # Adds +heading+ to the table of contents
- def accept_heading(heading)
- @res << heading unless suppressed? heading
- end
+ def accept_heading(heading)
+ @res << heading unless suppressed? heading
+ end
- ##
- # Returns the table of contents
+ ##
+ # Returns the table of contents
- def end_accepting
- @res
- end
+ def end_accepting
+ @res
+ end
- ##
- # Prepares the visitor for text generation
+ ##
+ # Prepares the visitor for text generation
- def start_accepting
- @omit_headings_below = nil
- @res = []
- end
+ def start_accepting
+ @omit_headings_below = nil
+ @res = []
+ end
- ##
- # Returns true if +heading+ is below the display threshold
+ ##
+ # Returns true if +heading+ is below the display threshold
- def suppressed?(heading)
- return false unless @omit_headings_below
+ def suppressed?(heading)
+ return false unless @omit_headings_below
- heading.level > @omit_headings_below
- end
+ heading.level > @omit_headings_below
+ end
- # :stopdoc:
- alias accept_block_quote ignore
- alias accept_raw ignore
- alias accept_rule ignore
- alias accept_blank_line ignore
- alias accept_paragraph ignore
- alias accept_verbatim ignore
- alias accept_list_end ignore
- alias accept_list_item_start ignore
- alias accept_list_item_end ignore
- alias accept_list_end_bullet ignore
- alias accept_list_start ignore
- alias accept_table ignore
- # :startdoc:
+ # :stopdoc:
+ alias accept_block_quote ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_blank_line ignore
+ alias accept_paragraph ignore
+ alias accept_verbatim ignore
+ alias accept_list_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_item_end ignore
+ alias accept_list_end_bullet ignore
+ alias accept_list_start ignore
+ alias accept_table ignore
+ # :startdoc:
+ end
+ end
end
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
index 218d1e0e36..b5eb64c955 100644
--- a/lib/rdoc/markup/to_test.rb
+++ b/lib/rdoc/markup/to_test.rb
@@ -1,77 +1,81 @@
# frozen_string_literal: true
-##
-# This Markup outputter is used for testing purposes.
+module RDoc
+ class Markup
+ ##
+ # This Markup outputter is used for testing purposes.
-class RDoc::Markup::ToTest < RDoc::Markup::Formatter
+ class ToTest < Markup::Formatter
- # :stopdoc:
+ # :stopdoc:
- ##
- # :section: Visitor
+ ##
+ # :section: Visitor
- def start_accepting
- @res = []
- @list = []
- end
+ def start_accepting
+ @res = []
+ @list = []
+ end
- def end_accepting
- @res
- end
+ def end_accepting
+ @res
+ end
- def handle_PLAIN_TEXT(text)
- @res << text
- end
+ def handle_PLAIN_TEXT(text)
+ @res << text
+ end
- def handle_REGEXP_HANDLING_TEXT(text)
- @res << text
- end
+ def handle_REGEXP_HANDLING_TEXT(text)
+ @res << text
+ end
- def accept_paragraph(paragraph)
- handle_inline(paragraph.text)
- end
+ def accept_paragraph(paragraph)
+ handle_inline(paragraph.text)
+ end
- def accept_raw(raw)
- @res << raw.parts.join
- end
+ def accept_raw(raw)
+ @res << raw.parts.join
+ end
- def accept_verbatim(verbatim)
- @res << verbatim.text.gsub(/^(\S)/, ' \1')
- end
+ def accept_verbatim(verbatim)
+ @res << verbatim.text.gsub(/^(\S)/, ' \1')
+ end
- def accept_list_start(list)
- @list << case list.type
- when :BULLET
- '*'
- when :NUMBER
- '1'
- else
- list.type
- end
- end
+ def accept_list_start(list)
+ @list << case list.type
+ when :BULLET
+ '*'
+ when :NUMBER
+ '1'
+ else
+ list.type
+ end
+ end
- def accept_list_end(list)
- @list.pop
- end
+ def accept_list_end(list)
+ @list.pop
+ end
- def accept_list_item_start(list_item)
- @res << "#{' ' * (@list.size - 1)}#{@list.last}: "
- end
+ def accept_list_item_start(list_item)
+ @res << "#{' ' * (@list.size - 1)}#{@list.last}: "
+ end
- def accept_list_item_end(list_item)
- end
+ def accept_list_item_end(list_item)
+ end
- def accept_blank_line(blank_line)
- @res << "\n"
- end
+ def accept_blank_line(blank_line)
+ @res << "\n"
+ end
- def accept_heading(heading)
- @res << "#{'=' * heading.level} #{heading.text}"
- end
+ def accept_heading(heading)
+ @res << "#{'=' * heading.level} #{heading.text}"
+ end
- def accept_rule(rule)
- @res << '-' * rule.weight
- end
+ def accept_rule(rule)
+ @res << '-' * rule.weight
+ end
- # :startdoc:
+ # :startdoc:
+ end
+ end
end
diff --git a/lib/rdoc/markup/to_tt_only.rb b/lib/rdoc/markup/to_tt_only.rb
index 960f5e65db..16f2566b99 100644
--- a/lib/rdoc/markup/to_tt_only.rb
+++ b/lib/rdoc/markup/to_tt_only.rb
@@ -1,107 +1,111 @@
# frozen_string_literal: true
-##
-# Extracts sections of text enclosed in plus, tt or code. Used to discover
-# undocumented parameters.
+module RDoc
+ class Markup
+ ##
+ # Extracts sections of text enclosed in plus, tt or code. Used to discover
+ # undocumented parameters.
-class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
+ class ToTtOnly < Markup::Formatter
- ##
- # Stack of list types
+ ##
+ # Stack of list types
- attr_reader :list_type
+ attr_reader :list_type
- ##
- # Output accumulator
+ ##
+ # Output accumulator
- attr_reader :res
+ attr_reader :res
- ##
- # Adds tts from +block_quote+ to the output
+ ##
+ # Adds tts from +block_quote+ to the output
- def accept_block_quote(block_quote)
- tt_sections block_quote.text
- end
+ def accept_block_quote(block_quote)
+ tt_sections block_quote.text
+ end
- ##
- # Pops the list type for +list+ from #list_type
+ ##
+ # Pops the list type for +list+ from #list_type
- def accept_list_end(list)
- @list_type.pop
- end
+ def accept_list_end(list)
+ @list_type.pop
+ end
- ##
- # Pushes the list type for +list+ onto #list_type
+ ##
+ # Pushes the list type for +list+ onto #list_type
- def accept_list_start(list)
- @list_type << list.type
- end
+ def accept_list_start(list)
+ @list_type << list.type
+ end
- ##
- # Prepares the visitor for consuming +list_item+
+ ##
+ # Prepares the visitor for consuming +list_item+
- def accept_list_item_start(list_item)
- case @list_type.last
- when :NOTE, :LABEL
- Array(list_item.label).map do |label|
- tt_sections label
- end.flatten
- end
- end
+ def accept_list_item_start(list_item)
+ case @list_type.last
+ when :NOTE, :LABEL
+ Array(list_item.label).map do |label|
+ tt_sections label
+ end.flatten
+ end
+ end
- ##
- # Adds +paragraph+ to the output
+ ##
+ # Adds +paragraph+ to the output
- def accept_paragraph(paragraph)
- tt_sections(paragraph.text)
- end
+ def accept_paragraph(paragraph)
+ tt_sections(paragraph.text)
+ end
- ##
- # Does nothing to +markup_item+ because it doesn't have any user-built
- # content
+ ##
+ # Does nothing to +markup_item+ because it doesn't have any user-built
+ # content
- def do_nothing(markup_item)
- end
+ def do_nothing(markup_item)
+ end
- alias accept_blank_line do_nothing # :nodoc:
- alias accept_heading do_nothing # :nodoc:
- alias accept_list_item_end do_nothing # :nodoc:
- alias accept_raw do_nothing # :nodoc:
- alias accept_rule do_nothing # :nodoc:
- alias accept_verbatim do_nothing # :nodoc:
-
- ##
- # Extracts tt sections from +text+
-
- def tt_sections(text)
- parsed = RDoc::Markup::InlineParser.new(text).parse
- traverse = -> node {
- next if String === node
- if node[:type] == :TT
- res << nil
- res << node[:children][0] || ''
- res << nil
- else
- node[:children].each(&traverse)
+ alias accept_blank_line do_nothing # :nodoc:
+ alias accept_heading do_nothing # :nodoc:
+ alias accept_list_item_end do_nothing # :nodoc:
+ alias accept_raw do_nothing # :nodoc:
+ alias accept_rule do_nothing # :nodoc:
+ alias accept_verbatim do_nothing # :nodoc:
+
+ ##
+ # Extracts tt sections from +text+
+
+ def tt_sections(text)
+ parsed = Markup::InlineParser.new(text).parse
+ traverse = -> node {
+ next if String === node
+ if node[:type] == :TT
+ res << nil
+ res << node[:children][0] || ''
+ res << nil
+ else
+ node[:children].each(&traverse)
+ end
+ }
+ parsed.each(&traverse)
+ res
end
- }
- parsed.each(&traverse)
- res
- end
- ##
- # Returns an Array of items that were wrapped in plus, tt or code.
+ ##
+ # Returns an Array of items that were wrapped in plus, tt or code.
- def end_accepting
- @res.compact
- end
+ def end_accepting
+ @res.compact
+ end
- ##
- # Prepares the visitor for gathering tt sections
+ ##
+ # Prepares the visitor for gathering tt sections
- def start_accepting
- @res = []
+ def start_accepting
+ @res = []
- @list_type = []
- end
+ @list_type = []
+ end
+ end
+ end
end
diff --git a/lib/rdoc/markup/verbatim.rb b/lib/rdoc/markup/verbatim.rb
index 3f4b9d48cd..c0b999f7d6 100644
--- a/lib/rdoc/markup/verbatim.rb
+++ b/lib/rdoc/markup/verbatim.rb
@@ -1,83 +1,87 @@
# frozen_string_literal: true
-##
-# A section of verbatim text
+module RDoc
+ class Markup
+ ##
+ # A section of verbatim text
-class RDoc::Markup::Verbatim < RDoc::Markup::Raw
+ class Verbatim < Markup::Raw
- ##
- # Format of this verbatim section
+ ##
+ # Format of this verbatim section
- attr_accessor :format
+ attr_accessor :format
- def initialize(*parts) # :nodoc:
- super
+ def initialize(*parts) # :nodoc:
+ super
- @format = nil
- end
-
- def ==(other) # :nodoc:
- super and @format == other.format
- end
+ @format = nil
+ end
- ##
- # Calls #accept_verbatim on +visitor+
+ def ==(other) # :nodoc:
+ super and @format == other.format
+ end
- def accept(visitor)
- visitor.accept_verbatim self
- end
+ ##
+ # Calls #accept_verbatim on +visitor+
- ##
- # Collapses 3+ newlines into two newlines
+ def accept(visitor)
+ visitor.accept_verbatim self
+ end
- def normalize
- parts = []
+ ##
+ # Collapses 3+ newlines into two newlines
- newlines = 0
+ def normalize
+ parts = []
- @parts.each do |part|
- case part
- when /^\s*\n/
- newlines += 1
- parts << part if newlines == 1
- else
newlines = 0
- parts << part
- end
- end
- parts.pop if parts.last =~ /\A\r?\n\z/
-
- @parts = parts
- end
+ @parts.each do |part|
+ case part
+ when /^\s*\n/
+ newlines += 1
+ parts << part if newlines == 1
+ else
+ newlines = 0
+ parts << part
+ end
+ end
- def pretty_print(q) # :nodoc:
- self.class.name =~ /.*::(\w{1,4})/i
+ parts.pop if parts.last =~ /\A\r?\n\z/
- q.group 2, "[#{$1.downcase}: ", ']' do
- if @format
- q.text "format: #{@format}"
- q.breakable
+ @parts = parts
end
- q.seplist @parts do |part|
- q.pp part
+ def pretty_print(q) # :nodoc:
+ self.class.name =~ /.*::(\w{1,4})/i
+
+ q.group 2, "[#{$1.downcase}: ", ']' do
+ if @format
+ q.text "format: #{@format}"
+ q.breakable
+ end
+
+ q.seplist @parts do |part|
+ q.pp part
+ end
+ end
end
- end
- end
- ##
- # Is this verbatim section Ruby code?
+ ##
+ # Is this verbatim section Ruby code?
- def ruby?
- @format ||= nil # TODO for older ri data, switch the tree to marshal_dump
- @format == :ruby || @format == :rb
- end
+ def ruby?
+ @format ||= nil # TODO for older ri data, switch the tree to marshal_dump
+ @format == :ruby || @format == :rb
+ end
- ##
- # The text of the section
+ ##
+ # The text of the section
- def text
- @parts.join
- end
+ def text
+ @parts.join
+ end
+ end
+ end
end
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index fe8053920c..8f4f9e37b1 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -2,730 +2,731 @@
require 'optparse'
require 'pathname'
-##
-# RDoc::Options handles the parsing and storage of options
-#
-# == Saved Options
-#
-# You can save some options like the markup format in the
-# .rdoc_options file in your gem. The easiest way to do this is:
-#
-# rdoc --markup tomdoc --write-options
-#
-# Which will automatically create the file and fill it with the options you
-# specified.
-#
-# The following options will not be saved since they interfere with the user's
-# preferences or with the normal operation of RDoc:
-#
-# * +--coverage-report+
-# * +--dry-run+
-# * +--encoding+
-# * +--force-update+
-# * +--format+
-# * +--pipe+
-# * +--quiet+
-# * +--template+
-# * +--verbose+
-#
-# == Custom Options
-#
-# Generators can hook into RDoc::Options to add generator-specific command
-# line options.
-#
-# When --format is encountered in ARGV, RDoc calls ::setup_options on
-# the generator class to add extra options to the option parser. Options for
-# custom generators must occur after --format. rdoc --help
-# will list options for all installed generators.
-#
-# Example:
-#
-# class RDoc::Generator::Spellcheck
-# RDoc::RDoc.add_generator self
-#
-# def self.setup_options rdoc_options
-# op = rdoc_options.option_parser
-#
-# op.on('--spell-dictionary DICTIONARY',
-# RDoc::Options::Path) do |dictionary|
-# rdoc_options.spell_dictionary = dictionary
-# end
-# end
-# end
-#
-# Of course, RDoc::Options does not respond to +spell_dictionary+ by default
-# so you will need to add it:
-#
-# class RDoc::Options
-#
-# ##
-# # The spell dictionary used by the spell-checking plugin.
-#
-# attr_accessor :spell_dictionary
-#
-# end
-#
-# == Option Validators
-#
-# OptionParser validators will validate and cast user input values. In
-# addition to the validators that ship with OptionParser (String, Integer,
-# Float, TrueClass, FalseClass, Array, Regexp, Date, Time, URI, etc.),
-# RDoc::Options adds Path, PathArray and Template.
-
-class RDoc::Options
-
+module RDoc
##
- # RDoc options ignored (or handled specially) by --write-options
-
- SPECIAL = %w[
- coverage_report
- dry_run
- encoding
- files
- force_output
- force_update
- generator
- generator_name
- generator_options
- generators
- locale
- op_dir
- page_dir
- option_parser
- pipe
- rdoc_include
- root
- server_port
- static_path
- template
- template_dir
- update_output_dir
- verbosity
- write_options
- ]
+ # RDoc::Options handles the parsing and storage of options
+ #
+ # == Saved Options
+ #
+ # You can save some options like the markup format in the
+ # .rdoc_options file in your gem. The easiest way to do this is:
+ #
+ # rdoc --markup tomdoc --write-options
+ #
+ # Which will automatically create the file and fill it with the options you
+ # specified.
+ #
+ # The following options will not be saved since they interfere with the user's
+ # preferences or with the normal operation of RDoc:
+ #
+ # * +--coverage-report+
+ # * +--dry-run+
+ # * +--encoding+
+ # * +--force-update+
+ # * +--format+
+ # * +--pipe+
+ # * +--quiet+
+ # * +--template+
+ # * +--verbose+
+ #
+ # == Custom Options
+ #
+ # Generators can hook into RDoc::Options to add generator-specific command
+ # line options.
+ #
+ # When --format is encountered in ARGV, RDoc calls ::setup_options on
+ # the generator class to add extra options to the option parser. Options for
+ # custom generators must occur after --format. rdoc --help
+ # will list options for all installed generators.
+ #
+ # Example:
+ #
+ # class RDoc::Generator::Spellcheck
+ # RDoc::RDoc.add_generator self
+ #
+ # def self.setup_options rdoc_options
+ # op = rdoc_options.option_parser
+ #
+ # op.on('--spell-dictionary DICTIONARY',
+ # RDoc::Options::Path) do |dictionary|
+ # rdoc_options.spell_dictionary = dictionary
+ # end
+ # end
+ # end
+ #
+ # Of course, RDoc::Options does not respond to +spell_dictionary+ by default
+ # so you will need to add it:
+ #
+ # class RDoc::Options
+ #
+ # ##
+ # # The spell dictionary used by the spell-checking plugin.
+ #
+ # attr_accessor :spell_dictionary
+ #
+ # end
+ #
+ # == Option Validators
+ #
+ # OptionParser validators will validate and cast user input values. In
+ # addition to the validators that ship with OptionParser (String, Integer,
+ # Float, TrueClass, FalseClass, Array, Regexp, Date, Time, URI, etc.),
+ # RDoc::Options adds Path, PathArray and Template.
+
+ class Options
+
+ ##
+ # RDoc options ignored (or handled specially) by --write-options
+
+ SPECIAL = %w[
+ coverage_report
+ dry_run
+ encoding
+ files
+ force_output
+ force_update
+ generator
+ generator_name
+ generator_options
+ generators
+ locale
+ op_dir
+ page_dir
+ option_parser
+ pipe
+ rdoc_include
+ root
+ server_port
+ static_path
+ template
+ template_dir
+ update_output_dir
+ verbosity
+ write_options
+ ]
- ##
- # Option validator for OptionParser that matches a directory that exists on
- # the filesystem.
+ ##
+ # Option validator for OptionParser that matches a directory that exists on
+ # the filesystem.
- Directory = Object.new
+ Directory = Object.new
- ##
- # Option validator for OptionParser that matches a file or directory that
- # exists on the filesystem.
+ ##
+ # Option validator for OptionParser that matches a file or directory that
+ # exists on the filesystem.
- Path = Object.new
+ Path = Object.new
- ##
- # Option validator for OptionParser that matches a comma-separated list of
- # files or directories that exist on the filesystem.
+ ##
+ # Option validator for OptionParser that matches a comma-separated list of
+ # files or directories that exist on the filesystem.
- PathArray = Object.new
+ PathArray = Object.new
- ##
- # Option validator for OptionParser that matches a template directory for an
- # installed generator that lives in
- # "rdoc/generator/template/#{template_name}"
+ ##
+ # Option validator for OptionParser that matches a template directory for an
+ # installed generator that lives in
+ # "rdoc/generator/template/#{template_name}"
- Template = Object.new
+ Template = Object.new
- ##
- # Character-set for HTML output. #encoding is preferred over #charset
+ ##
+ # Character-set for HTML output. #encoding is preferred over #charset
- attr_accessor :charset
+ attr_accessor :charset
- ##
- # If true, RDoc will not write any files.
+ ##
+ # If true, RDoc will not write any files.
- attr_accessor :dry_run
+ attr_accessor :dry_run
- ##
- # The output encoding. All input files will be transcoded to this encoding.
- #
- # The default encoding is UTF-8. This is set via --encoding.
+ ##
+ # The output encoding. All input files will be transcoded to this encoding.
+ #
+ # The default encoding is UTF-8. This is set via --encoding.
- attr_accessor :encoding
+ attr_accessor :encoding
- ##
- # Files matching this pattern will be excluded
+ ##
+ # Files matching this pattern will be excluded
- attr_writer :exclude
+ attr_writer :exclude
- ##
- # The list of files to be processed
+ ##
+ # The list of files to be processed
- attr_accessor :files
+ attr_accessor :files
- ##
- # Create the output even if the output directory does not look
- # like an rdoc output directory
+ ##
+ # Create the output even if the output directory does not look
+ # like an rdoc output directory
- attr_accessor :force_output
+ attr_accessor :force_output
- ##
- # Scan newer sources than the flag file if true.
+ ##
+ # Scan newer sources than the flag file if true.
- attr_accessor :force_update
+ attr_accessor :force_update
- ##
- # Formatter to mark up text with
+ ##
+ # Formatter to mark up text with
- attr_accessor :formatter
+ attr_accessor :formatter
- ##
- # Description of the output generator (set with the --format option)
+ ##
+ # Description of the output generator (set with the --format option)
- attr_accessor :generator
+ attr_accessor :generator
- ##
- # For #==
+ ##
+ # For #==
- attr_reader :generator_name # :nodoc:
+ attr_reader :generator_name # :nodoc:
- ##
- # Loaded generator options. Used to prevent --help from loading the same
- # options multiple times.
+ ##
+ # Loaded generator options. Used to prevent --help from loading the same
+ # options multiple times.
- attr_accessor :generator_options
+ attr_accessor :generator_options
- ##
- # Old rdoc behavior: hyperlink all words that match a method name,
- # even if not preceded by '#' or '::'
+ ##
+ # Old rdoc behavior: hyperlink all words that match a method name,
+ # even if not preceded by '#' or '::'
- attr_accessor :hyperlink_all
+ attr_accessor :hyperlink_all
- ##
- # Include line numbers in the source code
+ ##
+ # Include line numbers in the source code
- attr_accessor :line_numbers
+ attr_accessor :line_numbers
- ##
- # The output locale.
+ ##
+ # The output locale.
- attr_accessor :locale
+ attr_accessor :locale
- ##
- # The directory where locale data live.
+ ##
+ # The directory where locale data live.
- attr_accessor :locale_dir
+ attr_accessor :locale_dir
- ##
- # Name of the file, class or module to display in the initial index page (if
- # not specified the first file we encounter is used)
+ ##
+ # Name of the file, class or module to display in the initial index page (if
+ # not specified the first file we encounter is used)
- attr_accessor :main_page
-
- ##
- # The markup format.
- # One of: +rdoc+ (the default), +markdown+, +rd+, +tomdoc+.
- # See {Markup Formats}[rdoc-ref:RDoc::Markup@Markup+Formats].
- attr_accessor :markup
+ attr_accessor :main_page
- ##
- # If true, only report on undocumented files
+ ##
+ # The markup format.
+ # One of: +rdoc+ (the default), +markdown+, +rd+, +tomdoc+.
+ # See {Markup Formats}[rdoc-ref:RDoc::Markup@Markup+Formats].
+ attr_accessor :markup
- attr_accessor :coverage_report
+ ##
+ # If true, only report on undocumented files
- ##
- # The name of the output directory
+ attr_accessor :coverage_report
- attr_accessor :op_dir
+ ##
+ # The name of the output directory
- ##
- # The OptionParser for this instance
+ attr_accessor :op_dir
- attr_accessor :option_parser
+ ##
+ # The OptionParser for this instance
- ##
- # Output heading decorations?
- attr_accessor :output_decoration
+ attr_accessor :option_parser
- ##
- # Directory where guides, FAQ, and other pages not associated with a class
- # live. You may leave this unset if these are at the root of your project.
+ ##
+ # Output heading decorations?
+ attr_accessor :output_decoration
- attr_accessor :page_dir
+ ##
+ # Directory where guides, FAQ, and other pages not associated with a class
+ # live. You may leave this unset if these are at the root of your project.
- ##
- # Is RDoc in pipe mode?
+ attr_accessor :page_dir
- attr_accessor :pipe
+ ##
+ # Is RDoc in pipe mode?
- ##
- # Array of directories to search for files to satisfy an :include:
+ attr_accessor :pipe
- attr_accessor :rdoc_include
+ ##
+ # Array of directories to search for files to satisfy an :include:
- ##
- # Root of the source documentation will be generated for. Set this when
- # building documentation outside the source directory. Defaults to the
- # current directory.
+ attr_accessor :rdoc_include
- attr_accessor :root
+ ##
+ # Root of the source documentation will be generated for. Set this when
+ # building documentation outside the source directory. Defaults to the
+ # current directory.
- ##
- # Include the '#' at the front of hyperlinked instance method names
+ attr_accessor :root
- attr_accessor :show_hash
+ ##
+ # Include the '#' at the front of hyperlinked instance method names
- ##
- # Directory to copy static files from
+ attr_accessor :show_hash
- attr_accessor :static_path
+ ##
+ # Directory to copy static files from
- ##
- # The number of columns in a tab
+ attr_accessor :static_path
- attr_accessor :tab_width
+ ##
+ # The number of columns in a tab
- ##
- # Template to be used when generating output
+ attr_accessor :tab_width
- attr_accessor :template
+ ##
+ # Template to be used when generating output
- ##
- # Directory the template lives in
+ attr_accessor :template
- attr_accessor :template_dir
+ ##
+ # Directory the template lives in
- ##
- # Additional template stylesheets
+ attr_accessor :template_dir
- attr_accessor :template_stylesheets
+ ##
+ # Additional template stylesheets
- ##
- # Documentation title
+ attr_accessor :template_stylesheets
- attr_accessor :title
+ ##
+ # Documentation title
- ##
- # Should RDoc update the timestamps in the output dir?
+ attr_accessor :title
- attr_accessor :update_output_dir
+ ##
+ # Should RDoc update the timestamps in the output dir?
- ##
- # Verbosity, zero means quiet
+ attr_accessor :update_output_dir
- attr_accessor :verbosity
+ ##
+ # Verbosity, zero means quiet
- ##
- # Warn if rdoc-ref links can't be resolved
- # Default is +true+
+ attr_accessor :verbosity
- attr_accessor :warn_missing_rdoc_ref
+ ##
+ # Warn if rdoc-ref links can't be resolved
+ # Default is +true+
- ##
- # URL of web cvs frontend
+ attr_accessor :warn_missing_rdoc_ref
- attr_accessor :webcvs
+ ##
+ # URL of web cvs frontend
- ##
- # Minimum visibility of a documented method. One of +:public+, +:protected+,
- # +:private+ or +:nodoc+.
- #
- # The +:nodoc+ visibility ignores all directives related to visibility. The
- # other visibilities may be overridden on a per-method basis with the :doc:
- # directive.
+ attr_accessor :webcvs
- attr_reader :visibility
+ ##
+ # Minimum visibility of a documented method. One of +:public+, +:protected+,
+ # +:private+ or +:nodoc+.
+ #
+ # The +:nodoc+ visibility ignores all directives related to visibility. The
+ # other visibilities may be overridden on a per-method basis with the :doc:
+ # directive.
- ##
- # When set to a port number, starts a live-reloading server instead of
- # writing files. Defaults to +false+ (no server). Set via
- # --server[=PORT].
+ attr_reader :visibility
- attr_reader :server_port
+ ##
+ # When set to a port number, starts a live-reloading server instead of
+ # writing files. Defaults to +false+ (no server). Set via
+ # --server[=PORT].
- ##
- # Indicates if files of test suites should be skipped
- attr_accessor :skip_tests
+ attr_reader :server_port
- ##
- # Embed mixin methods, attributes, and constants into class documentation. Set via
- # +--[no-]embed-mixins+ (Default is +false+.)
- attr_accessor :embed_mixins
+ ##
+ # Indicates if files of test suites should be skipped
+ attr_accessor :skip_tests
- ##
- # Exclude the default patterns as well if true.
- attr_reader :apply_default_exclude
+ ##
+ # Embed mixin methods, attributes, and constants into class documentation. Set via
+ # +--[no-]embed-mixins+ (Default is +false+.)
+ attr_accessor :embed_mixins
- ##
- # Words to be ignored in autolink cross-references
- attr_accessor :autolink_excluded_words
+ ##
+ # Exclude the default patterns as well if true.
+ attr_reader :apply_default_exclude
- ##
- # The prefix to use for class and module page paths
+ ##
+ # Words to be ignored in autolink cross-references
+ attr_accessor :autolink_excluded_words
- attr_accessor :class_module_path_prefix
+ ##
+ # The prefix to use for class and module page paths
- ##
- # The prefix to use for file page paths
+ attr_accessor :class_module_path_prefix
- attr_accessor :file_path_prefix
+ ##
+ # The prefix to use for file page paths
- ##
- # The preferred root URL for the documentation
+ attr_accessor :file_path_prefix
- attr_accessor :canonical_root
+ ##
+ # The preferred root URL for the documentation
- ##
- # Custom footer content configuration for themes that support it.
- # Currently only supported by the Aliki theme.
- #
- # A hash where keys are column titles and values are hashes of link text => URL pairs.
- # Each column will be displayed in the upper footer section.
- #
- # Example:
- # {
- # "DOCUMENTATION" => {"Home" => "/index.html", "Guide" => "/guide.html"},
- # "RESOURCES" => {"RDoc" => "https://ruby.github.io/rdoc/", "GitHub" => "https://github.com/ruby/rdoc"}
- # }
+ attr_accessor :canonical_root
- attr_accessor :footer_content
+ ##
+ # Custom footer content configuration for themes that support it.
+ # Currently only supported by the Aliki theme.
+ #
+ # A hash where keys are column titles and values are hashes of link text => URL pairs.
+ # Each column will be displayed in the upper footer section.
+ #
+ # Example:
+ # {
+ # "DOCUMENTATION" => {"Home" => "/index.html", "Guide" => "/guide.html"},
+ # "RESOURCES" => {"RDoc" => "https://ruby.github.io/rdoc/", "GitHub" => "https://github.com/ruby/rdoc"}
+ # }
- def initialize(loaded_options = nil) # :nodoc:
- init_ivars
- override loaded_options if loaded_options
- end
+ attr_accessor :footer_content
- DEFAULT_EXCLUDE = %w[
- ~\z \.orig\z \.rej\z \.bak\z
- \.gemspec\z
- ]
-
- def init_ivars # :nodoc:
- @autolink_excluded_words = []
- @dry_run = false
- @embed_mixins = false
- @exclude = []
- @files = nil
- @force_output = false
- @force_update = true
- @generator_name = "aliki"
- @generators = RDoc::RDoc::GENERATORS
- @generator_options = []
- @hyperlink_all = false
- @line_numbers = false
- @locale = nil
- @locale_name = nil
- @locale_dir = 'locale'
- @main_page = nil
- @markup = 'rdoc'
- @coverage_report = false
- @op_dir = nil
- @page_dir = nil
- @pipe = false
- @output_decoration = true
- @rdoc_include = []
- @root = Pathname(Dir.pwd)
- @server_port = false
- @show_hash = false
- @static_path = []
- @tab_width = 8
- @template = nil
- @template_dir = nil
- @template_stylesheets = []
- @title = nil
- @update_output_dir = true
- @verbosity = 1
- @visibility = :protected
- @warn_missing_rdoc_ref = true
- @webcvs = nil
- @write_options = false
- @encoding = Encoding::UTF_8
- @charset = @encoding.name
- @skip_tests = true
- @apply_default_exclude = true
- @class_module_path_prefix = nil
- @file_path_prefix = nil
- @canonical_root = nil
- @footer_content = nil
- end
+ def initialize(loaded_options = nil) # :nodoc:
+ init_ivars
+ override loaded_options if loaded_options
+ end
- def init_with(map) # :nodoc:
- init_ivars
-
- encoding = map['encoding']
- @encoding = encoding ? Encoding.find(encoding) : encoding
-
- @charset = map['charset']
- @embed_mixins = map['embed_mixins']
- @exclude = map['exclude']
- @generator_name = map['generator_name']
- @hyperlink_all = map['hyperlink_all']
- @line_numbers = map['line_numbers']
- @locale_name = map['locale_name']
- @locale_dir = map['locale_dir']
- @main_page = map['main_page']
- @markup = map['markup']
- @op_dir = map['op_dir']
- @show_hash = map['show_hash']
- @tab_width = map['tab_width']
- @template_dir = map['template_dir']
- @title = map['title']
- @visibility = map['visibility']
- @webcvs = map['webcvs']
-
- @apply_default_exclude = map['apply_default_exclude']
- @autolink_excluded_words = map['autolink_excluded_words']
- @footer_content = map['footer_content']
-
- @rdoc_include = sanitize_path map['rdoc_include']
- @static_path = sanitize_path map['static_path']
- end
+ DEFAULT_EXCLUDE = %w[
+ ~\z \.orig\z \.rej\z \.bak\z
+ \.gemspec\z
+ ]
+
+ def init_ivars # :nodoc:
+ @autolink_excluded_words = []
+ @dry_run = false
+ @embed_mixins = false
+ @exclude = []
+ @files = nil
+ @force_output = false
+ @force_update = true
+ @generator_name = "aliki"
+ @generators = RDoc::GENERATORS
+ @generator_options = []
+ @hyperlink_all = false
+ @line_numbers = false
+ @locale = nil
+ @locale_name = nil
+ @locale_dir = 'locale'
+ @main_page = nil
+ @markup = 'rdoc'
+ @coverage_report = false
+ @op_dir = nil
+ @page_dir = nil
+ @pipe = false
+ @output_decoration = true
+ @rdoc_include = []
+ @root = Pathname(Dir.pwd)
+ @server_port = false
+ @show_hash = false
+ @static_path = []
+ @tab_width = 8
+ @template = nil
+ @template_dir = nil
+ @template_stylesheets = []
+ @title = nil
+ @update_output_dir = true
+ @verbosity = 1
+ @visibility = :protected
+ @warn_missing_rdoc_ref = true
+ @webcvs = nil
+ @write_options = false
+ @encoding = ::Encoding::UTF_8
+ @charset = @encoding.name
+ @skip_tests = true
+ @apply_default_exclude = true
+ @class_module_path_prefix = nil
+ @file_path_prefix = nil
+ @canonical_root = nil
+ @footer_content = nil
+ end
- def yaml_initialize(tag, map) # :nodoc:
- init_with map
- end
+ def init_with(map) # :nodoc:
+ init_ivars
- def override(map) # :nodoc:
- if map.has_key?('encoding')
encoding = map['encoding']
- @encoding = encoding ? Encoding.find(encoding) : encoding
- end
+ @encoding = encoding ? ::Encoding.find(encoding) : encoding
+
+ @charset = map['charset']
+ @embed_mixins = map['embed_mixins']
+ @exclude = map['exclude']
+ @generator_name = map['generator_name']
+ @hyperlink_all = map['hyperlink_all']
+ @line_numbers = map['line_numbers']
+ @locale_name = map['locale_name']
+ @locale_dir = map['locale_dir']
+ @main_page = map['main_page']
+ @markup = map['markup']
+ @op_dir = map['op_dir']
+ @show_hash = map['show_hash']
+ @tab_width = map['tab_width']
+ @template_dir = map['template_dir']
+ @title = map['title']
+ @visibility = map['visibility']
+ @webcvs = map['webcvs']
+
+ @apply_default_exclude = map['apply_default_exclude']
+ @autolink_excluded_words = map['autolink_excluded_words']
+ @footer_content = map['footer_content']
- @charset = map['charset'] if map.has_key?('charset')
- @embed_mixins = map['embed_mixins'] if map.has_key?('embed_mixins')
- @exclude = map['exclude'] if map.has_key?('exclude')
- @generator_name = map['generator_name'] if map.has_key?('generator_name')
- @hyperlink_all = map['hyperlink_all'] if map.has_key?('hyperlink_all')
- @line_numbers = map['line_numbers'] if map.has_key?('line_numbers')
- @locale_name = map['locale_name'] if map.has_key?('locale_name')
- @locale_dir = map['locale_dir'] if map.has_key?('locale_dir')
- @main_page = map['main_page'] if map.has_key?('main_page')
- @markup = map['markup'] if map.has_key?('markup')
- @op_dir = map['op_dir'] if map.has_key?('op_dir')
- @page_dir = map['page_dir'] if map.has_key?('page_dir')
- @show_hash = map['show_hash'] if map.has_key?('show_hash')
- @tab_width = map['tab_width'] if map.has_key?('tab_width')
- @template_dir = map['template_dir'] if map.has_key?('template_dir')
- @title = map['title'] if map.has_key?('title')
- @visibility = map['visibility'] if map.has_key?('visibility')
- @webcvs = map['webcvs'] if map.has_key?('webcvs')
- @autolink_excluded_words = map['autolink_excluded_words'] if map.has_key?('autolink_excluded_words')
- @apply_default_exclude = map['apply_default_exclude'] if map.has_key?('apply_default_exclude')
- @canonical_root = map['canonical_root'] if map.has_key?('canonical_root')
- @footer_content = map['footer_content'] if map.has_key?('footer_content')
-
- @warn_missing_rdoc_ref = map['warn_missing_rdoc_ref'] if map.has_key?('warn_missing_rdoc_ref')
-
- if map.has_key?('rdoc_include')
@rdoc_include = sanitize_path map['rdoc_include']
- end
- if map.has_key?('static_path')
@static_path = sanitize_path map['static_path']
end
- end
- def ==(other) # :nodoc:
- self.class === other and
- @encoding == other.encoding and
- @embed_mixins == other.embed_mixins and
- @generator_name == other.generator_name and
- @hyperlink_all == other.hyperlink_all and
- @line_numbers == other.line_numbers and
- @locale == other.locale and
- @locale_dir == other.locale_dir and
- @main_page == other.main_page and
- @markup == other.markup and
- @op_dir == other.op_dir and
- @rdoc_include == other.rdoc_include and
- @show_hash == other.show_hash and
- @static_path == other.static_path and
- @tab_width == other.tab_width and
- @template == other.template and
- @title == other.title and
- @visibility == other.visibility and
- @webcvs == other.webcvs and
- @apply_default_exclude == other.apply_default_exclude and
- @autolink_excluded_words == other.autolink_excluded_words
- end
+ def yaml_initialize(tag, map) # :nodoc:
+ init_with map
+ end
- ##
- # Check that the files on the command line exist
+ def override(map) # :nodoc:
+ if map.has_key?('encoding')
+ encoding = map['encoding']
+ @encoding = encoding ? ::Encoding.find(encoding) : encoding
+ end
+
+ @charset = map['charset'] if map.has_key?('charset')
+ @embed_mixins = map['embed_mixins'] if map.has_key?('embed_mixins')
+ @exclude = map['exclude'] if map.has_key?('exclude')
+ @generator_name = map['generator_name'] if map.has_key?('generator_name')
+ @hyperlink_all = map['hyperlink_all'] if map.has_key?('hyperlink_all')
+ @line_numbers = map['line_numbers'] if map.has_key?('line_numbers')
+ @locale_name = map['locale_name'] if map.has_key?('locale_name')
+ @locale_dir = map['locale_dir'] if map.has_key?('locale_dir')
+ @main_page = map['main_page'] if map.has_key?('main_page')
+ @markup = map['markup'] if map.has_key?('markup')
+ @op_dir = map['op_dir'] if map.has_key?('op_dir')
+ @page_dir = map['page_dir'] if map.has_key?('page_dir')
+ @show_hash = map['show_hash'] if map.has_key?('show_hash')
+ @tab_width = map['tab_width'] if map.has_key?('tab_width')
+ @template_dir = map['template_dir'] if map.has_key?('template_dir')
+ @title = map['title'] if map.has_key?('title')
+ @visibility = map['visibility'] if map.has_key?('visibility')
+ @webcvs = map['webcvs'] if map.has_key?('webcvs')
+ @autolink_excluded_words = map['autolink_excluded_words'] if map.has_key?('autolink_excluded_words')
+ @apply_default_exclude = map['apply_default_exclude'] if map.has_key?('apply_default_exclude')
+ @canonical_root = map['canonical_root'] if map.has_key?('canonical_root')
+ @footer_content = map['footer_content'] if map.has_key?('footer_content')
+
+ @warn_missing_rdoc_ref = map['warn_missing_rdoc_ref'] if map.has_key?('warn_missing_rdoc_ref')
+
+ if map.has_key?('rdoc_include')
+ @rdoc_include = sanitize_path map['rdoc_include']
+ end
+ if map.has_key?('static_path')
+ @static_path = sanitize_path map['static_path']
+ end
+ end
+
+ def ==(other) # :nodoc:
+ self.class === other and
+ @encoding == other.encoding and
+ @embed_mixins == other.embed_mixins and
+ @generator_name == other.generator_name and
+ @hyperlink_all == other.hyperlink_all and
+ @line_numbers == other.line_numbers and
+ @locale == other.locale and
+ @locale_dir == other.locale_dir and
+ @main_page == other.main_page and
+ @markup == other.markup and
+ @op_dir == other.op_dir and
+ @rdoc_include == other.rdoc_include and
+ @show_hash == other.show_hash and
+ @static_path == other.static_path and
+ @tab_width == other.tab_width and
+ @template == other.template and
+ @title == other.title and
+ @visibility == other.visibility and
+ @webcvs == other.webcvs and
+ @apply_default_exclude == other.apply_default_exclude and
+ @autolink_excluded_words == other.autolink_excluded_words
+ end
+
+ ##
+ # Check that the files on the command line exist
- def check_files
- @files.delete_if do |file|
- if File.exist? file
- if File.readable? file
- false
+ def check_files
+ @files.delete_if do |file|
+ if File.exist? file
+ if File.readable? file
+ false
+ else
+ warn "file '#{file}' not readable"
+
+ true
+ end
else
- warn "file '#{file}' not readable"
+ warn "file '#{file}' not found"
true
end
- else
- warn "file '#{file}' not found"
-
- true
end
end
- end
- ##
- # Ensure only one generator is loaded
+ ##
+ # Ensure only one generator is loaded
- def check_generator
- if @generator
- raise OptionParser::InvalidOption,
- "generator already set to #{@generator_name}"
+ def check_generator
+ if @generator
+ raise OptionParser::InvalidOption,
+ "generator already set to #{@generator_name}"
+ end
end
- end
- ##
- # Set the title, but only if not already set. Used to set the title
- # from a source file, so that a title set from the command line
- # will have the priority.
+ ##
+ # Set the title, but only if not already set. Used to set the title
+ # from a source file, so that a title set from the command line
+ # will have the priority.
- def default_title=(string)
- @title ||= string
- end
+ def default_title=(string)
+ @title ||= string
+ end
- ##
- # For dumping YAML
+ ##
+ # For dumping YAML
- def to_yaml(*options) # :nodoc:
- encoding = @encoding ? @encoding.name : nil
+ def to_yaml(*options) # :nodoc:
+ encoding = @encoding ? @encoding.name : nil
- yaml = {}
- yaml['encoding'] = encoding
- yaml['static_path'] = sanitize_path(@static_path)
- yaml['rdoc_include'] = sanitize_path(@rdoc_include)
- yaml['page_dir'] = (sanitize_path([@page_dir]).first if @page_dir)
+ yaml = {}
+ yaml['encoding'] = encoding
+ yaml['static_path'] = sanitize_path(@static_path)
+ yaml['rdoc_include'] = sanitize_path(@rdoc_include)
+ yaml['page_dir'] = (sanitize_path([@page_dir]).first if @page_dir)
- ivars = instance_variables.map { |ivar| ivar.to_s[1..-1] }
- ivars -= SPECIAL
+ ivars = instance_variables.map { |ivar| ivar.to_s[1..-1] }
+ ivars -= SPECIAL
- ivars.sort.each do |ivar|
- yaml[ivar] = instance_variable_get("@#{ivar}")
- end
+ ivars.sort.each do |ivar|
+ yaml[ivar] = instance_variable_get("@#{ivar}")
+ end
- if yaml.respond_to?(:to_yaml)
- yaml.to_yaml
- else
- RDoc.yaml_serializer.dump(yaml)
+ if yaml.respond_to?(:to_yaml)
+ yaml.to_yaml
+ else
+ ::RDoc.yaml_serializer.dump(yaml)
+ end
end
- end
- ##
- # Create a regexp for #exclude
-
- def exclude
- if @exclude.nil? or Regexp === @exclude
- # done, #finish is being re-run
- @exclude
- elsif !@apply_default_exclude and @exclude.empty?
- nil
- else
- exclude = @exclude
- exclude |= DEFAULT_EXCLUDE if @apply_default_exclude
- Regexp.new(exclude.join("|"))
+ ##
+ # Create a regexp for #exclude
+
+ def exclude
+ if @exclude.nil? or Regexp === @exclude
+ # done, #finish is being re-run
+ @exclude
+ elsif !@apply_default_exclude and @exclude.empty?
+ nil
+ else
+ exclude = @exclude
+ exclude |= DEFAULT_EXCLUDE if @apply_default_exclude
+ Regexp.new(exclude.join("|"))
+ end
end
- end
- ##
- # Completes any unfinished option setup business such as filtering for
- # existent files, creating a regexp for #exclude and setting a default
- # #template.
+ ##
+ # Completes any unfinished option setup business such as filtering for
+ # existent files, creating a regexp for #exclude and setting a default
+ # #template.
- def finish
- if @write_options
- write_options
- exit
- end
+ def finish
+ if @write_options
+ write_options
+ exit
+ end
- @op_dir ||= 'doc'
+ @op_dir ||= 'doc'
- root = @root.to_s
- if @rdoc_include.empty? || !@rdoc_include.include?(root)
- @rdoc_include << root
- end
+ root = @root.to_s
+ if @rdoc_include.empty? || !@rdoc_include.include?(root)
+ @rdoc_include << root
+ end
- @exclude = self.exclude
+ @exclude = self.exclude
- finish_page_dir
+ finish_page_dir
- check_files
+ check_files
- # If no template was specified, use the default template for the output
- # formatter
+ # If no template was specified, use the default template for the output
+ # formatter
- unless @template
- @template = @generator_name
- @template_dir = template_dir_for @template
- end
+ unless @template
+ @template = @generator_name
+ @template_dir = template_dir_for @template
+ end
- if @locale_name
- @locale = RDoc::I18n::Locale[@locale_name]
- @locale.load(@locale_dir)
- else
- @locale = nil
+ if @locale_name
+ @locale = I18n::Locale[@locale_name]
+ @locale.load(@locale_dir)
+ else
+ @locale = nil
+ end
+
+ self
end
- self
- end
+ ##
+ # Fixes the page_dir to be relative to the root_dir and adds the page_dir to
+ # the files list.
- ##
- # Fixes the page_dir to be relative to the root_dir and adds the page_dir to
- # the files list.
+ def finish_page_dir
+ return unless @page_dir
- def finish_page_dir
- return unless @page_dir
+ @files << @page_dir
- @files << @page_dir
+ page_dir = Pathname(@page_dir)
+ begin
+ page_dir = page_dir.expand_path.relative_path_from @root
+ rescue ArgumentError
+ # On Windows, sometimes crosses different drive letters.
+ page_dir = page_dir.expand_path
+ end
- page_dir = Pathname(@page_dir)
- begin
- page_dir = page_dir.expand_path.relative_path_from @root
- rescue ArgumentError
- # On Windows, sometimes crosses different drive letters.
- page_dir = page_dir.expand_path
+ @page_dir = page_dir
end
- @page_dir = page_dir
- end
-
- ##
- # Returns a properly-space list of generators and their descriptions.
+ ##
+ # Returns a properly-space list of generators and their descriptions.
- def generator_descriptions
- lengths = []
+ def generator_descriptions
+ lengths = []
- generators = RDoc::RDoc::GENERATORS.map do |name, generator|
- lengths << name.length
+ generators = RDoc::GENERATORS.map do |name, generator|
+ lengths << name.length
- description = generator::DESCRIPTION if
- generator.const_defined? :DESCRIPTION
+ description = generator::DESCRIPTION if
+ generator.const_defined? :DESCRIPTION
- [name, description]
- end
+ [name, description]
+ end
- longest = lengths.max
+ longest = lengths.max
- generators.sort.map do |name, description|
- if description
- " %-*s - %s" % [longest, name, description]
- else
- " #{name}"
- end
- end.join "\n"
- end
+ generators.sort.map do |name, description|
+ if description
+ " %-*s - %s" % [longest, name, description]
+ else
+ " #{name}"
+ end
+ end.join "\n"
+ end
- ##
- # Parses command line options.
+ ##
+ # Parses command line options.
- def parse(argv)
- ignore_invalid = true
+ def parse(argv)
+ ignore_invalid = true
- argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
+ argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
- opts = OptionParser.new do |opt|
- @option_parser = opt
- opt.program_name = File.basename $0
- opt.version = RDoc::VERSION
- opt.release = nil
- opt.summary_indent = ' ' * 4
- opt.banner = <<-EOF
+ opts = OptionParser.new do |opt|
+ @option_parser = opt
+ opt.program_name = File.basename $0
+ opt.version = VERSION
+ opt.release = nil
+ opt.summary_indent = ' ' * 4
+ opt.banner = <<-EOF
Usage: #{opt.program_name} [options] [names...]
Files are parsed, and the information they contain collected, before any
@@ -753,660 +754,661 @@ def parse(argv)
EOF
- parsers = Hash.new { |h, parser| h[parser] = [] }
-
- RDoc::Parser.parsers.each do |regexp, parser|
- parsers[parser.name.sub('RDoc::Parser::', '')] << regexp.source
- end
-
- parsers.sort.each do |parser, regexp|
- opt.banner += " - #{parser}: #{regexp.join ', '}\n"
- end
- opt.banner += " - TomDoc: Only in ruby files\n"
-
- opt.accept Template do |template|
- template_dir = template_dir_for template
+ parsers = Hash.new { |h, parser| h[parser] = [] }
- unless template_dir
- $stderr.puts "could not find template #{template}"
- nil
- else
- [template, template_dir]
+ Parser.parsers.each do |regexp, parser|
+ parsers[parser.name.sub('RDoc::Parser::', '')] << regexp.source
end
- end
-
- opt.accept Directory do |directory|
- directory = File.expand_path directory
- raise OptionParser::InvalidArgument unless File.directory? directory
+ parsers.sort.each do |parser, regexp|
+ opt.banner += " - #{parser}: #{regexp.join ', '}\n"
+ end
+ opt.banner += " - TomDoc: Only in ruby files\n"
- directory
- end
+ opt.accept Template do |template|
+ template_dir = template_dir_for template
- opt.accept Path do |path|
- path = File.expand_path path
+ unless template_dir
+ $stderr.puts "could not find template #{template}"
+ nil
+ else
+ [template, template_dir]
+ end
+ end
- raise OptionParser::InvalidArgument unless File.exist? path
+ opt.accept Directory do |directory|
+ directory = File.expand_path directory
- path
- end
+ raise OptionParser::InvalidArgument unless File.directory? directory
- opt.accept PathArray do |paths,|
- paths = if paths
- paths.split(',').map { |d| d unless d.empty? }
- end
+ directory
+ end
- paths.map do |path|
+ opt.accept Path do |path|
path = File.expand_path path
raise OptionParser::InvalidArgument unless File.exist? path
path
end
- end
- opt.separator nil
- opt.separator "Parsing options:"
- opt.separator nil
+ opt.accept PathArray do |paths,|
+ paths = if paths
+ paths.split(',').map { |d| d unless d.empty? }
+ end
- opt.on("--encoding=ENCODING", "-e", Encoding.list.map { |e| e.name },
- "Specifies the output encoding. All files",
- "read will be converted to this encoding.",
- "The default encoding is UTF-8.",
- "--encoding is preferred over --charset") do |value|
- @encoding = Encoding.find value
- @charset = @encoding.name # may not be valid value
- end
+ paths.map do |path|
+ path = File.expand_path path
- opt.separator nil
+ raise OptionParser::InvalidArgument unless File.exist? path
- opt.on("--locale=NAME",
- "Specifies the output locale.") do |value|
- @locale_name = value
- end
+ path
+ end
+ end
- opt.on("--locale-data-dir=DIR",
- "Specifies the directory where locale data live.") do |value|
- @locale_dir = value
- end
+ opt.separator nil
+ opt.separator "Parsing options:"
+ opt.separator nil
- opt.separator nil
+ opt.on("--encoding=ENCODING", "-e", ::Encoding.list.map { |e| e.name },
+ "Specifies the output encoding. All files",
+ "read will be converted to this encoding.",
+ "The default encoding is UTF-8.",
+ "--encoding is preferred over --charset") do |value|
+ @encoding = ::Encoding.find value
+ @charset = @encoding.name # may not be valid value
+ end
- opt.on("--all", "-a",
- "Synonym for --visibility=private.") do |value|
- @visibility = :private
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--locale=NAME",
+ "Specifies the output locale.") do |value|
+ @locale_name = value
+ end
- opt.on("--exclude=PATTERN", "-x", Regexp,
- "Do not process files or directories",
- "matching PATTERN.") do |value|
- @exclude << value
- end
+ opt.on("--locale-data-dir=DIR",
+ "Specifies the directory where locale data live.") do |value|
+ @locale_dir = value
+ end
- opt.on("--[no-]apply-default-exclude",
- "Use default PATTERN to exclude.") do |value|
- @apply_default_exclude = value
- end
+ opt.separator nil
+
+ opt.on("--all", "-a",
+ "Synonym for --visibility=private.") do |value|
+ @visibility = :private
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--no-skipping-tests", nil,
- "Don't skip generating documentation for test and spec files") do |value|
- @skip_tests = false
- end
+ opt.on("--exclude=PATTERN", "-x", Regexp,
+ "Do not process files or directories",
+ "matching PATTERN.") do |value|
+ @exclude << value
+ end
- opt.separator nil
+ opt.on("--[no-]apply-default-exclude",
+ "Use default PATTERN to exclude.") do |value|
+ @apply_default_exclude = value
+ end
- opt.on("--extension=NEW=OLD", "-E",
- "Treat files ending with .new as if they",
- "ended with .old. Using '-E cgi=rb' will",
- "cause xxx.cgi to be parsed as a Ruby file.") do |value|
- new, old = value.split(/=/, 2)
+ opt.separator nil
- unless new and old
- raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
+ opt.on("--no-skipping-tests", nil,
+ "Don't skip generating documentation for test and spec files") do |value|
+ @skip_tests = false
end
- unless RDoc::Parser.alias_extension old, new
- raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
+ opt.separator nil
+
+ opt.on("--extension=NEW=OLD", "-E",
+ "Treat files ending with .new as if they",
+ "ended with .old. Using '-E cgi=rb' will",
+ "cause xxx.cgi to be parsed as a Ruby file.") do |value|
+ new, old = value.split(/=/, 2)
+
+ unless new and old
+ raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
+ end
+
+ unless Parser.alias_extension old, new
+ raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
+ end
end
- end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]force-update", "-U",
- "Forces rdoc to scan all sources even if",
- "no files are newer than the flag file.") do |value|
- @force_update = value
- end
+ opt.on("--[no-]force-update", "-U",
+ "Forces rdoc to scan all sources even if",
+ "no files are newer than the flag file.") do |value|
+ @force_update = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--pipe", "-p",
- "Convert RDoc on stdin to HTML") do
- @pipe = true
- end
+ opt.on("--pipe", "-p",
+ "Convert RDoc on stdin to HTML") do
+ @pipe = true
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--tab-width=WIDTH", "-w", Integer,
- "Set the width of tab characters.") do |value|
- raise OptionParser::InvalidArgument,
- "#{value} is an invalid tab width" if value <= 0
- @tab_width = value
- end
+ opt.on("--tab-width=WIDTH", "-w", Integer,
+ "Set the width of tab characters.") do |value|
+ raise OptionParser::InvalidArgument,
+ "#{value} is an invalid tab width" if value <= 0
+ @tab_width = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--visibility=VISIBILITY", RDoc::VISIBILITIES + [:nodoc],
- "Minimum visibility to document a method.",
- "One of 'public', 'protected' (the default),",
- "'private' or 'nodoc' (show everything)") do |value|
- @visibility = value
- end
+ opt.on("--visibility=VISIBILITY", VISIBILITIES + [:nodoc],
+ "Minimum visibility to document a method.",
+ "One of 'public', 'protected' (the default),",
+ "'private' or 'nodoc' (show everything)") do |value|
+ @visibility = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]embed-mixins",
- "Embed mixin methods, attributes, and constants",
- "into class documentation. (default false)") do |value|
- @embed_mixins = value
- end
+ opt.on("--[no-]embed-mixins",
+ "Embed mixin methods, attributes, and constants",
+ "into class documentation. (default false)") do |value|
+ @embed_mixins = value
+ end
- opt.separator nil
+ opt.separator nil
- markup_formats = RDoc::Text::MARKUP_FORMAT.keys.sort
+ markup_formats = Text::MARKUP_FORMAT.keys.sort
- opt.on("--markup=MARKUP", markup_formats,
- "The markup format for the named files.",
- "The default is rdoc. Valid values are:",
- markup_formats.join(', ')) do |value|
- @markup = value
- end
+ opt.on("--markup=MARKUP", markup_formats,
+ "The markup format for the named files.",
+ "The default is rdoc. Valid values are:",
+ markup_formats.join(', ')) do |value|
+ @markup = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--root=ROOT", Directory,
- "Root of the source tree documentation",
- "will be generated for. Set this when",
- "building documentation outside the",
- "source directory. Default is the",
- "current directory.") do |root|
- @root = Pathname(root)
- end
+ opt.on("--root=ROOT", Directory,
+ "Root of the source tree documentation",
+ "will be generated for. Set this when",
+ "building documentation outside the",
+ "source directory. Default is the",
+ "current directory.") do |root|
+ @root = Pathname(root)
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--page-dir=DIR", Directory,
- "Directory where guides, your FAQ or",
- "other pages not associated with a class",
- "live. Set this when you don't store",
- "such files at your project root.",
- "NOTE: Do not use the same file name in",
- "the page dir and the root of your project") do |page_dir|
- @page_dir = page_dir
- end
+ opt.on("--page-dir=DIR", Directory,
+ "Directory where guides, your FAQ or",
+ "other pages not associated with a class",
+ "live. Set this when you don't store",
+ "such files at your project root.",
+ "NOTE: Do not use the same file name in",
+ "the page dir and the root of your project") do |page_dir|
+ @page_dir = page_dir
+ end
- opt.separator nil
- opt.separator "Common generator options:"
- opt.separator nil
+ opt.separator nil
+ opt.separator "Common generator options:"
+ opt.separator nil
- opt.on("--force-output", "-O",
- "Forces rdoc to write the output files,",
- "even if the output directory exists",
- "and does not seem to have been created",
- "by rdoc.") do |value|
- @force_output = value
- end
+ opt.on("--force-output", "-O",
+ "Forces rdoc to write the output files,",
+ "even if the output directory exists",
+ "and does not seem to have been created",
+ "by rdoc.") do |value|
+ @force_output = value
+ end
- opt.separator nil
+ opt.separator nil
- generator_text = @generators.keys.map { |name| " #{name}" }.sort
+ generator_text = @generators.keys.map { |name| " #{name}" }.sort
- opt.on("-f", "--fmt=FORMAT", "--format=FORMAT", @generators.keys,
- "Set the output formatter. One of:", *generator_text) do |value|
- check_generator
+ opt.on("-f", "--fmt=FORMAT", "--format=FORMAT", @generators.keys,
+ "Set the output formatter. One of:", *generator_text) do |value|
+ check_generator
- @generator_name = value.downcase
- setup_generator
- end
+ @generator_name = value.downcase
+ setup_generator
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--include=DIRECTORIES", "-i", PathArray,
- "Set (or add to) the list of directories to",
- "be searched when satisfying :include:",
- "requests. Can be used more than once.") do |value|
- @rdoc_include.concat value.map { |dir| dir.strip }
- end
+ opt.on("--include=DIRECTORIES", "-i", PathArray,
+ "Set (or add to) the list of directories to",
+ "be searched when satisfying :include:",
+ "requests. Can be used more than once.") do |value|
+ @rdoc_include.concat value.map { |dir| dir.strip }
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]coverage-report=[LEVEL]", "--[no-]dcov", "-C", Integer,
- "Prints a report on undocumented items.",
- "Does not generate files.") do |value|
- value = 0 if value.nil? # Integer converts -C to nil
+ opt.on("--[no-]coverage-report=[LEVEL]", "--[no-]dcov", "-C", Integer,
+ "Prints a report on undocumented items.",
+ "Does not generate files.") do |value|
+ value = 0 if value.nil? # Integer converts -C to nil
- @coverage_report = value
- @force_update = true if value
- end
+ @coverage_report = value
+ @force_update = true if value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--output=DIR", "--op", "-o",
- "Set the output directory.") do |value|
- @op_dir = value
- end
+ opt.on("--output=DIR", "--op", "-o",
+ "Set the output directory.") do |value|
+ @op_dir = value
+ end
- opt.separator nil
- opt.separator 'HTML generator options:'
- opt.separator nil
+ opt.separator nil
+ opt.separator 'HTML generator options:'
+ opt.separator nil
- opt.on("--charset=CHARSET", "-c",
- "Specifies the output HTML character-set.",
- "Use --encoding instead of --charset if",
- "available.") do |value|
- @charset = value
- end
+ opt.on("--charset=CHARSET", "-c",
+ "Specifies the output HTML character-set.",
+ "Use --encoding instead of --charset if",
+ "available.") do |value|
+ @charset = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--autolink-excluded-words=WORDS", Array,
- "Words to be ignored in autolink cross-references") do |value|
- @autolink_excluded_words.concat value
- end
+ opt.on("--autolink-excluded-words=WORDS", Array,
+ "Words to be ignored in autolink cross-references") do |value|
+ @autolink_excluded_words.concat value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--hyperlink-all", "-A",
- "Generate hyperlinks for all words that",
- "correspond to known methods, even if they",
- "do not start with '#' or '::' (legacy",
- "behavior).") do |value|
- @hyperlink_all = value
- end
+ opt.on("--hyperlink-all", "-A",
+ "Generate hyperlinks for all words that",
+ "correspond to known methods, even if they",
+ "do not start with '#' or '::' (legacy",
+ "behavior).") do |value|
+ @hyperlink_all = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--main=NAME", "-m",
- "NAME will be the initial page displayed.") do |value|
- @main_page = value
- end
+ opt.on("--main=NAME", "-m",
+ "NAME will be the initial page displayed.") do |value|
+ @main_page = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]line-numbers", "-N",
- "Include line numbers in the source code.",
- "By default, only the number of the first",
- "line is displayed, in a leading comment.") do |value|
- @line_numbers = value
- end
+ opt.on("--[no-]line-numbers", "-N",
+ "Include line numbers in the source code.",
+ "By default, only the number of the first",
+ "line is displayed, in a leading comment.") do |value|
+ @line_numbers = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--show-hash", "-H",
- "A name of the form #name in a comment is a",
- "possible hyperlink to an instance method",
- "name. When displayed, the '#' is removed",
- "unless this option is specified.") do |value|
- @show_hash = value
- end
+ opt.on("--show-hash", "-H",
+ "A name of the form #name in a comment is a",
+ "possible hyperlink to an instance method",
+ "name. When displayed, the '#' is removed",
+ "unless this option is specified.") do |value|
+ @show_hash = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--template=NAME", "-T", Template,
- "Set the template used when generating",
- "output. The default depends on the",
- "formatter used.") do |(template, template_dir)|
- @template = template
- @template_dir = template_dir
- end
+ opt.on("--template=NAME", "-T", Template,
+ "Set the template used when generating",
+ "output. The default depends on the",
+ "formatter used.") do |(template, template_dir)|
+ @template = template
+ @template_dir = template_dir
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--template-stylesheets=FILES", PathArray,
- "Set (or add to) the list of files to",
- "include with the html template.") do |value|
- @template_stylesheets.concat value
- end
+ opt.on("--template-stylesheets=FILES", PathArray,
+ "Set (or add to) the list of files to",
+ "include with the html template.") do |value|
+ @template_stylesheets.concat value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--title=TITLE", "-t",
- "Set TITLE as the title for HTML output.") do |value|
- @title = value
- end
+ opt.on("--title=TITLE", "-t",
+ "Set TITLE as the title for HTML output.") do |value|
+ @title = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--copy-files=PATH", Path,
- "Specify a file or directory to copy static",
- "files from.",
- "If a file is given it will be copied into",
- "the output dir. If a directory is given the",
- "entire directory will be copied.",
- "You can use this multiple times") do |value|
- @static_path << value
- end
+ opt.on("--copy-files=PATH", Path,
+ "Specify a file or directory to copy static",
+ "files from.",
+ "If a file is given it will be copied into",
+ "the output dir. If a directory is given the",
+ "entire directory will be copied.",
+ "You can use this multiple times") do |value|
+ @static_path << value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--webcvs=URL", "-W",
- "Specify a URL for linking to a web frontend",
- "to CVS. If the URL contains a '\%s', the",
- "name of the current file will be",
- "substituted; if the URL doesn't contain a",
- "'\%s', the filename will be appended to it.") do |value|
- @webcvs = value
- end
+ opt.on("--webcvs=URL", "-W",
+ "Specify a URL for linking to a web frontend",
+ "to CVS. If the URL contains a '\%s', the",
+ "name of the current file will be",
+ "substituted; if the URL doesn't contain a",
+ "'\%s', the filename will be appended to it.") do |value|
+ @webcvs = value
+ end
- opt.separator nil
- opt.separator "ri generator options:"
- opt.separator nil
-
- opt.on("--ri", "-r",
- "Generate output for use by `ri`. The files",
- "are stored in the '.rdoc' directory under",
- "your home directory unless overridden by a",
- "subsequent --op parameter, so no special",
- "privileges are needed.") do |value|
- check_generator
-
- @generator_name = "ri"
- @op_dir ||= RDoc::RI::Paths::HOMEDIR
- setup_generator
- end
+ opt.separator nil
+ opt.separator "ri generator options:"
+ opt.separator nil
+
+ opt.on("--ri", "-r",
+ "Generate output for use by `ri`. The files",
+ "are stored in the '.rdoc' directory under",
+ "your home directory unless overridden by a",
+ "subsequent --op parameter, so no special",
+ "privileges are needed.") do |value|
+ check_generator
+
+ @generator_name = "ri"
+ @op_dir ||= RI::Paths::HOMEDIR
+ setup_generator
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--ri-site", "-R",
- "Generate output for use by `ri`. The files",
- "are stored in a site-wide directory,",
- "making them accessible to others, so",
- "special privileges are needed.") do |value|
- check_generator
+ opt.on("--ri-site", "-R",
+ "Generate output for use by `ri`. The files",
+ "are stored in a site-wide directory,",
+ "making them accessible to others, so",
+ "special privileges are needed.") do |value|
+ check_generator
- @generator_name = "ri"
- @op_dir = RDoc::RI::Paths.site_dir
- setup_generator
- end
+ @generator_name = "ri"
+ @op_dir = RI::Paths.site_dir
+ setup_generator
+ end
- opt.separator nil
- opt.separator "Generic options:"
- opt.separator nil
+ opt.separator nil
+ opt.separator "Generic options:"
+ opt.separator nil
- opt.on("--server[=PORT]", Integer,
- "Start a web server to preview",
- "documentation with live reload.",
- "Defaults to port 4000.") do |port|
- @server_port = port || 4000
- end
+ opt.on("--server[=PORT]", Integer,
+ "Start a web server to preview",
+ "documentation with live reload.",
+ "Defaults to port 4000.") do |port|
+ @server_port = port || 4000
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--write-options",
- "Write .rdoc_options to the current",
- "directory with the given options. Not all",
- "options will be used. See RDoc::Options",
- "for details.") do |value|
- @write_options = true
- end
+ opt.on("--write-options",
+ "Write .rdoc_options to the current",
+ "directory with the given options. Not all",
+ "options will be used. See RDoc::Options",
+ "for details.") do |value|
+ @write_options = true
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]dry-run",
- "Don't write any files") do |value|
- @dry_run = value
- end
+ opt.on("--[no-]dry-run",
+ "Don't write any files") do |value|
+ @dry_run = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("-D", "--[no-]debug",
- "Displays lots on internal stuff.") do |value|
- $DEBUG_RDOC = value
- end
+ opt.on("-D", "--[no-]debug",
+ "Displays lots on internal stuff.") do |value|
+ $DEBUG_RDOC = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--warn-missing-rdoc-ref",
- "Warn if rdoc-ref links can't be resolved") do |value|
- @warn_missing_rdoc_ref = value
- end
+ opt.on("--warn-missing-rdoc-ref",
+ "Warn if rdoc-ref links can't be resolved") do |value|
+ @warn_missing_rdoc_ref = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]ignore-invalid",
- "Ignore invalid options and continue",
- "(default true).") do |value|
- ignore_invalid = value
- end
+ opt.on("--[no-]ignore-invalid",
+ "Ignore invalid options and continue",
+ "(default true).") do |value|
+ ignore_invalid = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--quiet", "-q",
- "Don't show progress as we parse.") do |value|
- @verbosity = 0
- end
+ opt.on("--quiet", "-q",
+ "Don't show progress as we parse.") do |value|
+ @verbosity = 0
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--verbose", "-V",
- "Display extra progress as RDoc parses") do |value|
- @verbosity = 2
- end
+ opt.on("--verbose", "-V",
+ "Display extra progress as RDoc parses") do |value|
+ @verbosity = 2
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--version", "-v", "print the version") do
- puts opt.version
- exit
- end
+ opt.on("--version", "-v", "print the version") do
+ puts opt.version
+ exit
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--help", "-h", "Display this help") do
- RDoc::RDoc::GENERATORS.each_key do |generator|
- setup_generator generator
+ opt.on("--help", "-h", "Display this help") do
+ RDoc::GENERATORS.each_key do |generator|
+ setup_generator generator
+ end
+
+ puts opt.help
+ exit
end
- puts opt.help
- exit
+ opt.separator nil
end
- opt.separator nil
- end
+ invalid = []
- invalid = []
+ begin
+ opts.parse! argv
+ rescue OptionParser::ParseError => e
+ if %w[--format --ri -r --ri-site -R].include? e.args.first
+ raise
+ else
+ invalid << e.args.join(' ')
+ end
- begin
- opts.parse! argv
- rescue OptionParser::ParseError => e
- if %w[--format --ri -r --ri-site -R].include? e.args.first
- raise
- else
- invalid << e.args.join(' ')
+ retry
end
- retry
- end
+ setup_generator unless @generator
- setup_generator unless @generator
-
- if @pipe and not argv.empty?
- @pipe = false
- invalid << '-p (with files)'
- end
+ if @pipe and not argv.empty?
+ @pipe = false
+ invalid << '-p (with files)'
+ end
- unless invalid.empty?
- invalid = "invalid options: #{invalid.join ', '}"
+ unless invalid.empty?
+ invalid = "invalid options: #{invalid.join ', '}"
- if ignore_invalid
- unless quiet
+ if ignore_invalid
+ unless quiet
+ $stderr.puts invalid
+ $stderr.puts '(invalid options are ignored)'
+ end
+ else
+ unless quiet
+ $stderr.puts opts
+ end
$stderr.puts invalid
- $stderr.puts '(invalid options are ignored)'
- end
- else
- unless quiet
- $stderr.puts opts
+ exit 1
end
- $stderr.puts invalid
- exit 1
end
- end
-
- @files = argv.dup
- self
- end
-
- ##
- # Don't display progress as we process the files
+ @files = argv.dup
- def quiet
- @verbosity.zero?
- end
+ self
+ end
- ##
- # Set quietness to +bool+
+ ##
+ # Don't display progress as we process the files
- def quiet=(bool)
- @verbosity = bool ? 0 : 1
- end
+ def quiet
+ @verbosity.zero?
+ end
- ##
- # Removes directories from +path+ that are outside the current directory
+ ##
+ # Set quietness to +bool+
- def sanitize_path(path)
- require 'pathname'
- dot = Pathname.new('.').expand_path
+ def quiet=(bool)
+ @verbosity = bool ? 0 : 1
+ end
- path.reject do |item|
- path = Pathname.new(item).expand_path
- is_reject = nil
- relative = nil
- begin
- relative = path.relative_path_from(dot).to_s
- rescue ArgumentError
- # On Windows, sometimes crosses different drive letters.
- is_reject = true
- else
- is_reject = relative.start_with? '..'
+ ##
+ # Removes directories from +path+ that are outside the current directory
+
+ def sanitize_path(path)
+ require 'pathname'
+ dot = Pathname.new('.').expand_path
+
+ path.reject do |item|
+ path = Pathname.new(item).expand_path
+ is_reject = nil
+ relative = nil
+ begin
+ relative = path.relative_path_from(dot).to_s
+ rescue ArgumentError
+ # On Windows, sometimes crosses different drive letters.
+ is_reject = true
+ else
+ is_reject = relative.start_with? '..'
+ end
+ is_reject
end
- is_reject
end
- end
- ##
- # Set up an output generator for the named +generator_name+.
- #
- # If the found generator responds to :setup_options it will be called with
- # the options instance. This allows generators to add custom options or set
- # default options.
+ ##
+ # Set up an output generator for the named +generator_name+.
+ #
+ # If the found generator responds to :setup_options it will be called with
+ # the options instance. This allows generators to add custom options or set
+ # default options.
- def setup_generator(generator_name = @generator_name)
- @generator = @generators[generator_name]
+ def setup_generator(generator_name = @generator_name)
+ @generator = @generators[generator_name]
- unless @generator
- raise OptionParser::InvalidArgument,
- "Invalid output formatter #{generator_name}"
- end
+ unless @generator
+ raise OptionParser::InvalidArgument,
+ "Invalid output formatter #{generator_name}"
+ end
- return if @generator_options.include? @generator
+ return if @generator_options.include? @generator
- @generator_name = generator_name
- @generator_options << @generator
+ @generator_name = generator_name
+ @generator_options << @generator
- if @generator.respond_to? :setup_options
- @option_parser ||= OptionParser.new
- @generator.setup_options self
+ if @generator.respond_to? :setup_options
+ @option_parser ||= OptionParser.new
+ @generator.setup_options self
+ end
end
- end
- ##
- # Finds the template dir for +template+
+ ##
+ # Finds the template dir for +template+
- def template_dir_for(template)
- template_path = File.join 'rdoc', 'generator', 'template', template
+ def template_dir_for(template)
+ template_path = File.join 'rdoc', 'generator', 'template', template
- $LOAD_PATH.map do |path|
- File.join File.expand_path(path), template_path
- end.find do |dir|
- File.directory? dir
+ $LOAD_PATH.map do |path|
+ File.join File.expand_path(path), template_path
+ end.find do |dir|
+ File.directory? dir
+ end
end
- end
- # Sets the minimum visibility of a documented method.
- #
- # Accepts +:public+, +:protected+, +:private+, +:nodoc+, or +:all+.
- #
- # When +:all+ is passed, visibility is set to +:private+, similarly to
- # RDOCOPT="--all", see #visibility for more information.
-
- def visibility=(visibility)
- case visibility
- when :all
- @visibility = :private
- else
- @visibility = visibility
+ # Sets the minimum visibility of a documented method.
+ #
+ # Accepts +:public+, +:protected+, +:private+, +:nodoc+, or +:all+.
+ #
+ # When +:all+ is passed, visibility is set to +:private+, similarly to
+ # RDOCOPT="--all", see #visibility for more information.
+
+ def visibility=(visibility)
+ case visibility
+ when :all
+ @visibility = :private
+ else
+ @visibility = visibility
+ end
end
- end
- ##
- # Displays a warning using Kernel#warn if we're being verbose
+ ##
+ # Displays a warning using Kernel#warn if we're being verbose
- def warn(message)
- super message if @verbosity > 1
- end
+ def warn(message)
+ super message if @verbosity > 1
+ end
- ##
- # Writes the YAML file .rdoc_options to the current directory containing the
- # parsed options.
+ ##
+ # Writes the YAML file .rdoc_options to the current directory containing the
+ # parsed options.
- def write_options
- RDoc.load_yaml
+ def write_options
+ ::RDoc.load_yaml
- File.open '.rdoc_options', 'w' do |io|
- io.set_encoding Encoding::UTF_8
+ File.open '.rdoc_options', 'w' do |io|
+ io.set_encoding ::Encoding::UTF_8
- io.print to_yaml
+ io.print to_yaml
+ end
end
- end
- ##
- # Loads options from .rdoc_options if the file exists, otherwise creates a
- # new RDoc::Options instance.
+ ##
+ # Loads options from .rdoc_options if the file exists, otherwise creates a
+ # new RDoc::Options instance.
- def self.load_options
- options_file = File.expand_path '.rdoc_options'
- return RDoc::Options.new unless File.exist? options_file
+ def self.load_options
+ options_file = File.expand_path '.rdoc_options'
+ return Options.new unless File.exist? options_file
- RDoc.load_yaml
+ ::RDoc.load_yaml
- content = File.read('.rdoc_options')
+ content = File.read('.rdoc_options')
- if defined?(Psych)
- begin
- options = Psych.safe_load content, permitted_classes: [RDoc::Options, Symbol]
- rescue Psych::SyntaxError
- raise RDoc::Error, "#{options_file} is not a valid rdoc options file"
+ if defined?(Psych)
+ begin
+ options = Psych.safe_load content, permitted_classes: [Options, Symbol]
+ rescue Psych::SyntaxError
+ raise Error, "#{options_file} is not a valid rdoc options file"
+ end
+ else
+ options = ::RDoc.yaml_serializer.load(content)
end
- else
- options = RDoc.yaml_serializer.load(content)
- end
- return RDoc::Options.new unless options # Allow empty file.
+ return Options.new unless options # Allow empty file.
- raise RDoc::Error, "#{options_file} is not a valid rdoc options file" unless
- RDoc::Options === options or Hash === options
+ raise Error, "#{options_file} is not a valid rdoc options file" unless
+ Options === options or Hash === options
- if Hash === options
- # Override the default values with the contents of YAML file.
- options = RDoc::Options.new options
+ if Hash === options
+ # Override the default values with the contents of YAML file.
+ options = Options.new options
+ end
+
+ options
end
- options
end
-
end
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 5c6ce16ace..7c7831b51b 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -1,294 +1,296 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: true
-##
-# A parser is simple a class that subclasses RDoc::Parser and implements #scan
-# to fill in an RDoc::TopLevel with parsed data.
-#
-# The initialize method takes an RDoc::TopLevel to fill with parsed content,
-# the name of the file to be parsed, the content of the file, an RDoc::Options
-# object and an RDoc::Stats object to inform the user of parsed items. The
-# scan method is then called to parse the file and must return the
-# RDoc::TopLevel object. By calling super these items will be set for you.
-#
-# In order to be used by RDoc the parser needs to register the file extensions
-# it can parse. Use ::parse_files_matching to register extensions.
-#
-# require 'rdoc'
-#
-# class RDoc::Parser::Xyz < RDoc::Parser
-# parse_files_matching /\.xyz$/
-#
-# def initialize top_level, file_name, content, options, stats
-# super
-#
-# # extra initialization if needed
-# end
-#
-# def scan
-# # parse file and fill in @top_level
-# end
-# end
-
-class RDoc::Parser
-
- @parsers = []
-
- class << self
+module RDoc
+ ##
+ # A parser is simple a class that subclasses RDoc::Parser and implements #scan
+ # to fill in an RDoc::TopLevel with parsed data.
+ #
+ # The initialize method takes an RDoc::TopLevel to fill with parsed content,
+ # the name of the file to be parsed, the content of the file, an RDoc::Options
+ # object and an RDoc::Stats object to inform the user of parsed items. The
+ # scan method is then called to parse the file and must return the
+ # RDoc::TopLevel object. By calling super these items will be set for you.
+ #
+ # In order to be used by RDoc the parser needs to register the file extensions
+ # it can parse. Use ::parse_files_matching to register extensions.
+ #
+ # require 'rdoc'
+ #
+ # class RDoc::Parser::Xyz < RDoc::Parser
+ # parse_files_matching /\.xyz$/
+ #
+ # def initialize top_level, file_name, content, options, stats
+ # super
+ #
+ # # extra initialization if needed
+ # end
+ #
+ # def scan
+ # # parse file and fill in @top_level
+ # end
+ # end
- ##
- # An Array of arrays that maps file extension (or name) regular
- # expressions to parser classes that will parse matching filenames.
- #
- # Use parse_files_matching to register a parser's file extensions.
+ class Parser
- attr_reader :parsers
+ @parsers = []
- end
+ class << self
- ##
- # The name of the file being parsed
+ ##
+ # An Array of arrays that maps file extension (or name) regular
+ # expressions to parser classes that will parse matching filenames.
+ #
+ # Use parse_files_matching to register a parser's file extensions.
- attr_reader :file_name
+ attr_reader :parsers
- ##
- # Alias an extension to another extension. After this call, files ending
- # "new_ext" will be parsed using the same parser as "old_ext"
+ end
- def self.alias_extension(old_ext, new_ext)
- old_ext = old_ext.sub(/^\.(.*)/, '\1')
- new_ext = new_ext.sub(/^\.(.*)/, '\1')
+ ##
+ # The name of the file being parsed
- parser = can_parse_by_name "xxx.#{old_ext}"
- return false unless parser
+ attr_reader :file_name
- RDoc::Parser.parsers.unshift [/\.#{new_ext}$/, parser]
+ ##
+ # Alias an extension to another extension. After this call, files ending
+ # "new_ext" will be parsed using the same parser as "old_ext"
- true
- end
+ def self.alias_extension(old_ext, new_ext)
+ old_ext = old_ext.sub(/^\.(.*)/, '\1')
+ new_ext = new_ext.sub(/^\.(.*)/, '\1')
- ##
- # Determines if the file is a "binary" file which basically means it has
- # content that an RDoc parser shouldn't try to consume.
+ parser = can_parse_by_name "xxx.#{old_ext}"
+ return false unless parser
- def self.binary?(file)
- return false if file =~ /\.(rdoc|txt)$/
+ Parser.parsers.unshift [/\.#{new_ext}$/, parser]
- s = File.read(file, 1024) or return false
+ true
+ end
- return true if s[0, 2] == Marshal.dump('')[0, 2] or s.index("\x00")
+ ##
+ # Determines if the file is a "binary" file which basically means it has
+ # content that an RDoc parser shouldn't try to consume.
- mode = 'r:utf-8' # default source encoding has been changed to utf-8
- s.sub!(/\A#!.*\n/, '') # assume shebang line isn't longer than 1024.
- encoding = s[/^\s*\#\s*(?:-\*-\s*)?(?:en)?coding:\s*([^\s;]+?)(?:-\*-|[\s;])/, 1]
- mode = "rb:#{encoding}" if encoding
- s = File.open(file, mode) {|f| f.gets(nil, 1024)}
+ def self.binary?(file)
+ return false if file =~ /\.(rdoc|txt)$/
- not s.valid_encoding?
- end
+ s = File.read(file, 1024) or return false
- ##
- # Checks if +file+ is a zip file in disguise. Signatures from
- # http://www.garykessler.net/library/file_sigs.html
+ return true if s[0, 2] == Marshal.dump('')[0, 2] or s.index("\x00")
- def self.zip?(file)
- zip_signature = File.read file, 4
+ mode = 'r:utf-8' # default source encoding has been changed to utf-8
+ s.sub!(/\A#!.*\n/, '') # assume shebang line isn't longer than 1024.
+ encoding = s[/^\s*\#\s*(?:-\*-\s*)?(?:en)?coding:\s*([^\s;]+?)(?:-\*-|[\s;])/, 1]
+ mode = "rb:#{encoding}" if encoding
+ s = File.open(file, mode) {|f| f.gets(nil, 1024)}
- zip_signature == "PK\x03\x04" or
- zip_signature == "PK\x05\x06" or
- zip_signature == "PK\x07\x08"
- rescue
- false
- end
+ not s.valid_encoding?
+ end
- ##
- # Return a parser that can handle a particular extension
+ ##
+ # Checks if +file+ is a zip file in disguise. Signatures from
+ # http://www.garykessler.net/library/file_sigs.html
- def self.can_parse(file_name)
- parser = can_parse_by_name file_name
+ def self.zip?(file)
+ zip_signature = File.read file, 4
- # HACK Selenium hides a jar file using a .txt extension
- return if parser == RDoc::Parser::Simple and zip? file_name
+ zip_signature == "PK\x03\x04" or
+ zip_signature == "PK\x05\x06" or
+ zip_signature == "PK\x07\x08"
+ rescue
+ false
+ end
- parser
- end
+ ##
+ # Return a parser that can handle a particular extension
- ##
- # Returns a parser that can handle the extension for +file_name+. This does
- # not depend upon the file being readable.
+ def self.can_parse(file_name)
+ parser = can_parse_by_name file_name
- def self.can_parse_by_name(file_name)
- _, parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }
+ # HACK Selenium hides a jar file using a .txt extension
+ return if parser == Parser::Simple and zip? file_name
- # The default parser must not parse binary files
- ext_name = File.extname file_name
- return parser if ext_name.empty?
+ parser
+ end
- if parser == RDoc::Parser::Simple and ext_name !~ /txt|rdoc/
- case mode = check_modeline(file_name)
- when nil, 'rdoc' # continue
- else
- RDoc::Parser.parsers.find { |_, p| return p if mode.casecmp?(p.name[/\w+\z/]) }
- return nil
+ ##
+ # Returns a parser that can handle the extension for +file_name+. This does
+ # not depend upon the file being readable.
+
+ def self.can_parse_by_name(file_name)
+ _, parser = Parser.parsers.find { |regexp,| regexp =~ file_name }
+
+ # The default parser must not parse binary files
+ ext_name = File.extname file_name
+ return parser if ext_name.empty?
+
+ if parser == Parser::Simple and ext_name !~ /txt|rdoc/
+ case mode = check_modeline(file_name)
+ when nil, 'rdoc' # continue
+ else
+ Parser.parsers.find { |_, p| return p if mode.casecmp?(p.name[/\w+\z/]) }
+ return nil
+ end
end
- end
- parser
- rescue Errno::EACCES
- end
+ parser
+ rescue Errno::EACCES
+ end
- ##
- # Returns the file type from the modeline in +file_name+
+ ##
+ # Returns the file type from the modeline in +file_name+
- def self.check_modeline(file_name)
- line = File.open file_name do |io|
- io.gets
- end
+ def self.check_modeline(file_name)
+ line = File.open file_name do |io|
+ io.gets
+ end
- /-\*-\s*(.*?\S)\s*-\*-/ =~ line
+ /-\*-\s*(.*?\S)\s*-\*-/ =~ line
- return nil unless type = $1
+ return nil unless type = $1
- if /;/ =~ type
- return nil unless /(?:\s|\A)mode:\s*([^\s;]+)/i =~ type
- type = $1
- end
+ if /;/ =~ type
+ return nil unless /(?:\s|\A)mode:\s*([^\s;]+)/i =~ type
+ type = $1
+ end
- return nil if /coding:/i =~ type
+ return nil if /coding:/i =~ type
- type.downcase
- rescue ArgumentError
- rescue Encoding::InvalidByteSequenceError # invalid byte sequence
+ type.downcase
+ rescue ArgumentError
+ rescue ::Encoding::InvalidByteSequenceError # invalid byte sequence
- end
+ end
- ##
- # Finds and instantiates the correct parser for the given +file_name+ and
- # +content+.
+ ##
+ # Finds and instantiates the correct parser for the given +file_name+ and
+ # +content+.
- def self.for(top_level, content, options, stats)
- file_name = top_level.absolute_name
- return if binary? file_name
+ def self.for(top_level, content, options, stats)
+ file_name = top_level.absolute_name
+ return if binary? file_name
- parser = use_markup content
+ parser = use_markup content
- unless parser
- parse_name = file_name
+ unless parser
+ parse_name = file_name
- # If no extension, look for shebang
- if file_name !~ /\.\w+$/ && content =~ %r{\A#!(.+)}
- shebang = $1
- case shebang
- when %r{env\s+ruby}, %r{/ruby}
- parse_name = 'dummy.rb'
+ # If no extension, look for shebang
+ if file_name !~ /\.\w+$/ && content =~ %r{\A#!(.+)}
+ shebang = $1
+ case shebang
+ when %r{env\s+ruby}, %r{/ruby}
+ parse_name = 'dummy.rb'
+ end
end
+
+ parser = can_parse parse_name
end
- parser = can_parse parse_name
- end
+ return unless parser
- return unless parser
+ content = remove_modeline content
- content = remove_modeline content
+ parser.new top_level, content, options, stats
+ rescue SystemCallError
+ nil
+ end
- parser.new top_level, content, options, stats
- rescue SystemCallError
- nil
- end
+ ##
+ # Record which file types this parser can understand.
+ #
+ # It is ok to call this multiple times.
- ##
- # Record which file types this parser can understand.
- #
- # It is ok to call this multiple times.
+ def self.parse_files_matching(regexp)
+ Parser.parsers.unshift [regexp, self]
+ end
- def self.parse_files_matching(regexp)
- RDoc::Parser.parsers.unshift [regexp, self]
- end
+ ##
+ # Removes an emacs-style modeline from the first line of the document
- ##
- # Removes an emacs-style modeline from the first line of the document
+ def self.remove_modeline(content)
+ content.sub(/\A.*-\*-\s*(.*?\S)\s*-\*-.*\r?\n/, '')
+ end
- def self.remove_modeline(content)
- content.sub(/\A.*-\*-\s*(.*?\S)\s*-\*-.*\r?\n/, '')
- end
+ ##
+ # If there is a markup: parser_name comment at the front of the
+ # file, use it to determine the parser. For example:
+ #
+ # # markup: rdoc
+ # # Class comment can go here
+ #
+ # class C
+ # end
+ #
+ # The comment should appear as the first line of the +content+.
+ #
+ # If the content contains a shebang or editor modeline the comment may
+ # appear on the second or third line.
+ #
+ # Any comment style may be used to hide the markup comment.
+ #
+ # The +tomdoc+ and +markdown+ markups name comment formats rather than
+ # parsers, so no parser is selected for them and RDoc::Parser.for picks one
+ # from the file name instead.
- ##
- # If there is a markup: parser_name comment at the front of the
- # file, use it to determine the parser. For example:
- #
- # # markup: rdoc
- # # Class comment can go here
- #
- # class C
- # end
- #
- # The comment should appear as the first line of the +content+.
- #
- # If the content contains a shebang or editor modeline the comment may
- # appear on the second or third line.
- #
- # Any comment style may be used to hide the markup comment.
- #
- # The +tomdoc+ and +markdown+ markups name comment formats rather than
- # parsers, so no parser is selected for them and RDoc::Parser.for picks one
- # from the file name instead.
+ def self.use_markup(content)
+ markup = content.lines.first(3).grep(/markup:\s+(\w+)/) { $1 }.first
- def self.use_markup(content)
- markup = content.lines.first(3).grep(/markup:\s+(\w+)/) { $1 }.first
+ return unless markup
- return unless markup
+ # tomdoc and markdown name a comment format, not a parser. Skipping them
+ # keeps the search below from matching RDoc::Parser::Markdown for a file
+ # that is not markdown.
+ return if %w[tomdoc markdown].include? markup
- # tomdoc and markdown name a comment format, not a parser. Skipping them
- # keeps the search below from matching RDoc::Parser::Markdown for a file
- # that is not markdown.
- return if %w[tomdoc markdown].include? markup
+ markup = Regexp.escape markup
- markup = Regexp.escape markup
+ _, selected = Parser.parsers.find do |_, parser|
+ /^#{markup}$/i =~ parser.name.sub(/.*:/, '')
+ end
- _, selected = RDoc::Parser.parsers.find do |_, parser|
- /^#{markup}$/i =~ parser.name.sub(/.*:/, '')
+ selected
end
- selected
- end
-
- ##
- # Creates a new Parser storing +top_level+, +file_name+, +content+,
- # +options+ and +stats+ in instance variables. In +@preprocess+ an
- # RDoc::Markup::PreProcess object is created which allows processing of
- # directives.
-
- def initialize(top_level, content, options, stats)
- @top_level = top_level
- @top_level.parser = self.class
- @store = @top_level.store
-
- @file_name = top_level.absolute_name
- @content = content
- @options = options
- @stats = stats
-
- @preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
- @preprocess.options = @options
- end
+ ##
+ # Creates a new Parser storing +top_level+, +file_name+, +content+,
+ # +options+ and +stats+ in instance variables. In +@preprocess+ an
+ # RDoc::Markup::PreProcess object is created which allows processing of
+ # directives.
+
+ def initialize(top_level, content, options, stats)
+ @top_level = top_level
+ @top_level.parser = self.class
+ @store = @top_level.store
+
+ @file_name = top_level.absolute_name
+ @content = content
+ @options = options
+ @stats = stats
+
+ @preprocess = Markup::PreProcess.new @file_name, @options.rdoc_include
+ @preprocess.options = @options
+ end
- autoload :Text, "#{__dir__}/parser/text"
+ autoload :Text, "#{__dir__}/parser/text"
- ##
- # Normalizes tabs in +body+
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = @options.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) do
- b, e = $~.offset(0)
- ' ' * (tab_width * (e-b) - b % tab_width)
- end
- line
- end.join "\n"
- else
- body
+ ##
+ # Normalizes tabs in +body+
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) do
+ b, e = $~.offset(0)
+ ' ' * (tab_width * (e-b) - b % tab_width)
+ end
+ line
+ end.join "\n"
+ else
+ body
+ end
end
end
end
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index 72762be5d6..c8bc98e6ae 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -1,1225 +1,1229 @@
# frozen_string_literal: true
require 'tsort'
-##
-# RDoc::Parser::C attempts to parse C extension files. It looks for
-# the standard patterns that you find in extensions: +rb_define_class+,
-# +rb_define_method+ and so on. It tries to find the corresponding
-# C source for the methods and extract comments, but if we fail
-# we don't worry too much.
-#
-# The comments associated with a Ruby method are extracted from the C
-# comment block associated with the routine that _implements_ that
-# method, that is to say the method whose name is given in the
-# +rb_define_method+ call. For example, you might write:
-#
-# /*
-# * Returns a new array that is a one-dimensional flattening of this
-# * array (recursively). That is, for every element that is an array,
-# * extract its elements into the new array.
-# *
-# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
-# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
-# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
-# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-# */
-# static VALUE
-# rb_ary_flatten(VALUE ary)
-# {
-# ary = rb_obj_dup(ary);
-# rb_ary_flatten_bang(ary);
-# return ary;
-# }
-#
-# ...
-#
-# void
-# Init_Array(void)
-# {
-# ...
-# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
-#
-# Here RDoc will determine from the +rb_define_method+ line that there's a
-# method called "flatten" in class Array, and will look for the implementation
-# in the method +rb_ary_flatten+. It will then use the comment from that
-# method in the HTML output. This method must be in the same source file
-# as the +rb_define_method+.
-#
-# The comment blocks may include special directives:
-#
-# [Document-class: +name+]
-# Documentation for the named class.
-#
-# [Document-module: +name+]
-# Documentation for the named module.
-#
-# [Document-const: +name+]
-# Documentation for the named +rb_define_const+.
-#
-# Constant values can be supplied on the first line of the comment like so:
-#
-# /* 300: The highest possible score in bowling */
-# rb_define_const(cFoo, "PERFECT", INT2FIX(300));
-#
-# The value can contain internal colons so long as they are escaped with a \
-#
-# [Document-global: +name+]
-# Documentation for the named +rb_define_global_const+
-#
-# [Document-variable: +name+]
-# Documentation for the named +rb_define_variable+
-#
-# [Document-method\: +method_name+]
-# Documentation for the named method. Use this when the method name is
-# unambiguous.
-#
-# [Document-method\: ClassName::method_name]
-# Documentation for a singleton method in the given class. Use this when
-# the method name alone is ambiguous.
-#
-# [Document-method\: ClassName#method_name]
-# Documentation for a instance method in the given class. Use this when the
-# method name alone is ambiguous.
-#
-# [Document-attr: +name+]
-# Documentation for the named attribute.
-#
-# [call-seq: text up to an empty line]
-# Because C source doesn't give descriptive names to Ruby-level parameters,
-# you need to document the calling sequence explicitly
-#
-# In addition, RDoc assumes by default that the C method implementing a
-# Ruby function is in the same source file as the rb_define_method call.
-# If this isn't the case, add the comment:
-#
-# rb_define_method(....); // in filename
-#
-# As an example, we might have an extension that defines multiple classes
-# in its Init_xxx method. We could document them using
-#
-# /*
-# * Document-class: MyClass
-# *
-# * Encapsulate the writing and reading of the configuration
-# * file. ...
-# */
-#
-# /*
-# * Document-method: read_value
-# *
-# * call-seq:
-# * cfg.read_value(key) -> value
-# * cfg.read_value(key} { |key| } -> value
-# *
-# * Return the value corresponding to +key+ from the configuration.
-# * In the second form, if the key isn't found, invoke the
-# * block and return its value.
-# */
-
-class RDoc::Parser::C < RDoc::Parser
-
- parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
-
- include RDoc::Text
-
- # :stopdoc:
- BOOL_ARG_PATTERN = /\s*+\b([01]|Q?(?:true|false)|TRUE|FALSE)\b\s*/
- TRUE_VALUES = ['1', 'TRUE', 'true', 'Qtrue'].freeze
- # :startdoc:
-
- ##
- # Maps C variable names to names of Ruby classes or modules
-
- attr_reader :classes
-
- ##
- # C file the parser is parsing
-
- attr_accessor :content
-
- ##
- # Dependencies from a missing enclosing class to the classes in
- # missing_dependencies that depend upon it.
-
- attr_reader :enclosure_dependencies
-
- ##
- # Maps C variable names to names of Ruby classes (and singleton classes)
-
- attr_reader :known_classes
-
- ##
- # Classes found while parsing the C file that were not yet registered due to
- # a missing enclosing class. These are processed by do_missing
-
- attr_reader :missing_dependencies
-
- ##
- # Maps C variable names to names of Ruby singleton classes
-
- attr_reader :singleton_classes
-
- ##
- # The TopLevel items in the parsed file belong to
-
- attr_reader :top_level
-
- ##
- # Prepares for parsing a C file. See RDoc::Parser#initialize for details on
- # the arguments.
-
- def initialize(top_level, content, options, stats)
- super
-
- @known_classes = RDoc::KNOWN_CLASSES.dup
- @content = handle_tab_width handle_ifdefs_in @content
- @file_dir = File.dirname @file_name
+module RDoc
+ class Parser
+ ##
+ # RDoc::Parser::C attempts to parse C extension files. It looks for
+ # the standard patterns that you find in extensions: +rb_define_class+,
+ # +rb_define_method+ and so on. It tries to find the corresponding
+ # C source for the methods and extract comments, but if we fail
+ # we don't worry too much.
+ #
+ # The comments associated with a Ruby method are extracted from the C
+ # comment block associated with the routine that _implements_ that
+ # method, that is to say the method whose name is given in the
+ # +rb_define_method+ call. For example, you might write:
+ #
+ # /*
+ # * Returns a new array that is a one-dimensional flattening of this
+ # * array (recursively). That is, for every element that is an array,
+ # * extract its elements into the new array.
+ # *
+ # * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+ # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+ # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+ # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # */
+ # static VALUE
+ # rb_ary_flatten(VALUE ary)
+ # {
+ # ary = rb_obj_dup(ary);
+ # rb_ary_flatten_bang(ary);
+ # return ary;
+ # }
+ #
+ # ...
+ #
+ # void
+ # Init_Array(void)
+ # {
+ # ...
+ # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+ #
+ # Here RDoc will determine from the +rb_define_method+ line that there's a
+ # method called "flatten" in class Array, and will look for the implementation
+ # in the method +rb_ary_flatten+. It will then use the comment from that
+ # method in the HTML output. This method must be in the same source file
+ # as the +rb_define_method+.
+ #
+ # The comment blocks may include special directives:
+ #
+ # [Document-class: +name+]
+ # Documentation for the named class.
+ #
+ # [Document-module: +name+]
+ # Documentation for the named module.
+ #
+ # [Document-const: +name+]
+ # Documentation for the named +rb_define_const+.
+ #
+ # Constant values can be supplied on the first line of the comment like so:
+ #
+ # /* 300: The highest possible score in bowling */
+ # rb_define_const(cFoo, "PERFECT", INT2FIX(300));
+ #
+ # The value can contain internal colons so long as they are escaped with a \
+ #
+ # [Document-global: +name+]
+ # Documentation for the named +rb_define_global_const+
+ #
+ # [Document-variable: +name+]
+ # Documentation for the named +rb_define_variable+
+ #
+ # [Document-method\: +method_name+]
+ # Documentation for the named method. Use this when the method name is
+ # unambiguous.
+ #
+ # [Document-method\: ClassName::method_name]
+ # Documentation for a singleton method in the given class. Use this when
+ # the method name alone is ambiguous.
+ #
+ # [Document-method\: ClassName#method_name]
+ # Documentation for a instance method in the given class. Use this when the
+ # method name alone is ambiguous.
+ #
+ # [Document-attr: +name+]
+ # Documentation for the named attribute.
+ #
+ # [call-seq: text up to an empty line]
+ # Because C source doesn't give descriptive names to Ruby-level parameters,
+ # you need to document the calling sequence explicitly
+ #
+ # In addition, RDoc assumes by default that the C method implementing a
+ # Ruby function is in the same source file as the rb_define_method call.
+ # If this isn't the case, add the comment:
+ #
+ # rb_define_method(....); // in filename
+ #
+ # As an example, we might have an extension that defines multiple classes
+ # in its Init_xxx method. We could document them using
+ #
+ # /*
+ # * Document-class: MyClass
+ # *
+ # * Encapsulate the writing and reading of the configuration
+ # * file. ...
+ # */
+ #
+ # /*
+ # * Document-method: read_value
+ # *
+ # * call-seq:
+ # * cfg.read_value(key) -> value
+ # * cfg.read_value(key} { |key| } -> value
+ # *
+ # * Return the value corresponding to +key+ from the configuration.
+ # * In the second form, if the key isn't found, invoke the
+ # * block and return its value.
+ # */
+
+ class C < Parser
+
+ parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
+
+ include ::RDoc::Text
+
+ # :stopdoc:
+ BOOL_ARG_PATTERN = /\s*+\b([01]|Q?(?:true|false)|TRUE|FALSE)\b\s*/
+ TRUE_VALUES = ['1', 'TRUE', 'true', 'Qtrue'].freeze
+ # :startdoc:
+
+ ##
+ # Maps C variable names to names of Ruby classes or modules
+
+ attr_reader :classes
+
+ ##
+ # C file the parser is parsing
+
+ attr_accessor :content
+
+ ##
+ # Dependencies from a missing enclosing class to the classes in
+ # missing_dependencies that depend upon it.
+
+ attr_reader :enclosure_dependencies
+
+ ##
+ # Maps C variable names to names of Ruby classes (and singleton classes)
+
+ attr_reader :known_classes
+
+ ##
+ # Classes found while parsing the C file that were not yet registered due to
+ # a missing enclosing class. These are processed by do_missing
+
+ attr_reader :missing_dependencies
+
+ ##
+ # Maps C variable names to names of Ruby singleton classes
+
+ attr_reader :singleton_classes
+
+ ##
+ # The TopLevel items in the parsed file belong to
+
+ attr_reader :top_level
+
+ ##
+ # Prepares for parsing a C file. See RDoc::Parser#initialize for details on
+ # the arguments.
+
+ def initialize(top_level, content, options, stats)
+ super
+
+ @known_classes = KNOWN_CLASSES.dup
+ @content = handle_tab_width handle_ifdefs_in @content
+ @file_dir = File.dirname @file_name
- @classes = load_variable_map :c_class_variables
- @singleton_classes = load_variable_map :c_singleton_class_variables
+ @classes = load_variable_map :c_class_variables
+ @singleton_classes = load_variable_map :c_singleton_class_variables
- @markup = @options.markup
+ @markup = @options.markup
- # class_variable => { function => [method, ...] }
- @methods = Hash.new { |h, f| h[f] = Hash.new { |i, m| i[m] = [] } }
+ # class_variable => { function => [method, ...] }
+ @methods = Hash.new { |h, f| h[f] = Hash.new { |i, m| i[m] = [] } }
- # missing variable => [handle_class_module arguments]
- @missing_dependencies = {}
+ # missing variable => [handle_class_module arguments]
+ @missing_dependencies = {}
- # missing enclosure variable => [dependent handle_class_module arguments]
- @enclosure_dependencies = Hash.new { |h, k| h[k] = [] }
- @enclosure_dependencies.instance_variable_set :@missing_dependencies,
- @missing_dependencies
+ # missing enclosure variable => [dependent handle_class_module arguments]
+ @enclosure_dependencies = Hash.new { |h, k| h[k] = [] }
+ @enclosure_dependencies.instance_variable_set :@missing_dependencies,
+ @missing_dependencies
- @enclosure_dependencies.extend TSort
+ @enclosure_dependencies.extend TSort
- def @enclosure_dependencies.tsort_each_node(&block)
- each_key(&block)
- rescue TSort::Cyclic => e
- cycle_vars = e.message.scan(/"(.*?)"/).flatten
+ def @enclosure_dependencies.tsort_each_node(&block)
+ each_key(&block)
+ rescue TSort::Cyclic => e
+ cycle_vars = e.message.scan(/"(.*?)"/).flatten
- cycle = cycle_vars.sort.map do |var_name|
- delete var_name
+ cycle = cycle_vars.sort.map do |var_name|
+ delete var_name
- var_name, type, mod_name, = @missing_dependencies[var_name]
+ var_name, type, mod_name, = @missing_dependencies[var_name]
- "#{type} #{mod_name} (#{var_name})"
- end.join ', '
+ "#{type} #{mod_name} (#{var_name})"
+ end.join ', '
- warn "Unable to create #{cycle} due to a cyclic class or module creation"
+ warn "Unable to create #{cycle} due to a cyclic class or module creation"
- retry
- end
-
- def @enclosure_dependencies.tsort_each_child(node, &block)
- fetch(node, []).each(&block)
- end
- end
+ retry
+ end
- ##
- # Scans #content for rb_define_alias
-
- def do_aliases
- @content.scan(/rb_define_alias\s*\(
- \s*(\w+),
- \s*"(.+?)",
- \s*"(.+?)"
- \s*\)/xm) do |var_name, new_name, old_name|
- class_name = @known_classes[var_name]
-
- unless class_name
- @options.warn "Enclosing class or module %p for alias %s %s is not known" % [
- var_name, new_name, old_name]
- next
+ def @enclosure_dependencies.tsort_each_child(node, &block)
+ fetch(node, []).each(&block)
+ end
end
- class_obj = find_class var_name, class_name
- comment = find_alias_comment var_name, new_name, old_name
- comment.normalize
- if comment.to_s.empty? and existing_method = class_obj.method_list.find { |m| m.name == old_name}
- comment = existing_method.comment
+ ##
+ # Scans #content for rb_define_alias
+
+ def do_aliases
+ @content.scan(/rb_define_alias\s*\(
+ \s*(\w+),
+ \s*"(.+?)",
+ \s*"(.+?)"
+ \s*\)/xm) do |var_name, new_name, old_name|
+ class_name = @known_classes[var_name]
+
+ unless class_name
+ @options.warn "Enclosing class or module %p for alias %s %s is not known" % [
+ var_name, new_name, old_name]
+ next
+ end
+
+ class_obj = find_class var_name, class_name
+ comment = find_alias_comment var_name, new_name, old_name
+ comment.normalize
+ if comment.to_s.empty? and existing_method = class_obj.method_list.find { |m| m.name == old_name}
+ comment = existing_method.comment
+ end
+ add_alias(var_name, class_obj, old_name, new_name, comment, singleton: @singleton_classes.key?(var_name))
+ end
end
- add_alias(var_name, class_obj, old_name, new_name, comment, singleton: @singleton_classes.key?(var_name))
- end
- end
- ##
- # Add alias, either from a direct alias definition, or from two
- # method that reference the same function.
+ ##
+ # Add alias, either from a direct alias definition, or from two
+ # method that reference the same function.
- def add_alias(var_name, class_obj, old_name, new_name, comment, singleton:)
- al = RDoc::Alias.new old_name, new_name, comment, singleton: singleton
- al.record_location @top_level
- class_obj.add_alias al
- @stats.add_alias al
- al
- end
+ def add_alias(var_name, class_obj, old_name, new_name, comment, singleton:)
+ al = Alias.new old_name, new_name, comment, singleton: singleton
+ al.record_location @top_level
+ class_obj.add_alias al
+ @stats.add_alias al
+ al
+ end
- ##
- # Scans #content for rb_attr and rb_define_attr
-
- def do_attrs
- @content.scan(/rb_attr\s*\(
- \s*(\w+),
- \s*([\w"()]+),
- #{BOOL_ARG_PATTERN},
- #{BOOL_ARG_PATTERN},
- \s*\w+\);/xmo) do |var_name, attr_name, read, write|
- handle_attr var_name, attr_name, read, write
- end
+ ##
+ # Scans #content for rb_attr and rb_define_attr
+
+ def do_attrs
+ @content.scan(/rb_attr\s*\(
+ \s*(\w+),
+ \s*([\w"()]+),
+ #{BOOL_ARG_PATTERN},
+ #{BOOL_ARG_PATTERN},
+ \s*\w+\);/xmo) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
+ end
- @content.scan(%r%rb_define_attr\(
- \s*([\w\.]+),
- \s*"([^"]+)",
- #{BOOL_ARG_PATTERN},
- #{BOOL_ARG_PATTERN}\);
- %xmo) do |var_name, attr_name, read, write|
- handle_attr var_name, attr_name, read, write
- end
- end
+ @content.scan(%r%rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ #{BOOL_ARG_PATTERN},
+ #{BOOL_ARG_PATTERN}\);
+ %xmo) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
+ end
+ end
- ##
- # Scans #content for boot_defclass
+ ##
+ # Scans #content for boot_defclass
- def do_boot_defclass
- @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
- |var_name, class_name, parent|
- parent = nil if parent == "0"
- handle_class_module(var_name, :class, class_name, parent, nil)
- end
- end
+ def do_boot_defclass
+ @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
+ |var_name, class_name, parent|
+ parent = nil if parent == "0"
+ handle_class_module(var_name, :class, class_name, parent, nil)
+ end
+ end
- ##
- # Scans #content for rb_define_class, boot_defclass, rb_define_class_under
- # and rb_singleton_class
-
- def do_classes_and_modules
- do_boot_defclass if @file_name == "class.c"
-
- @content.scan(
- %r(
- (?\s*\(\s*) {0}
- (?\s*\)\s*) {0}
- (?\s*"(?\w+)") {0}
- (?\s*(?:
- (?[\w\*\s\(\)\.\->]+) |
- rb_path2class\s*\(\s*"(?[\w:]+)"\s*\)
- )) {0}
- (?\w+) {0}
-
- (?[\w\.]+)\s* =
- \s*rb_(?:
- define_(?:
- class(?: # rb_define_class(name, parent_name)
- \(\s*
+ ##
+ # Scans #content for rb_define_class, boot_defclass, rb_define_class_under
+ # and rb_singleton_class
+
+ def do_classes_and_modules
+ do_boot_defclass if @file_name == "class.c"
+
+ @content.scan(
+ %r(
+ (?\s*\(\s*) {0}
+ (?\s*\)\s*) {0}
+ (?\s*"(?\w+)") {0}
+ (?\s*(?:
+ (?[\w\*\s\(\)\.\->]+) |
+ rb_path2class\s*\(\s*"(?[\w:]+)"\s*\)
+ )) {0}
+ (?\w+) {0}
+
+ (?[\w\.]+)\s* =
+ \s*rb_(?:
+ define_(?:
+ class(?: # rb_define_class(name, parent_name)
+ \(\s*
+ \g,
+ \g
+ \s*\)
+ |
+ _under\g # rb_define_class_under(under, name, parent_name...)
+ \g,
+ \g,
+ \g
+ \g
+ )
+ |
+ (?)
+ module(?: # rb_define_module(name)
+ \g
+ \g
+ \g
+ |
+ _under\g # rb_define_module_under(under, name)
+ \g,
+ \g
+ \g
+ )
+ )
+ |
+ (?(?:\s*"\w+",)*\s*NULL\s*) {0}
+ struct_define(?:
+ \g # rb_struct_define(name, ...)
\g,
- \g
- \s*\)
|
- _under\g # rb_define_class_under(under, name, parent_name...)
+ _under\g # rb_struct_define_under(under, name, ...)
\g,
\g,
- \g
- \g
- )
- |
- (?)
- module(?: # rb_define_module(name)
- \g
- \g
- \g
|
- _under\g # rb_define_module_under(under, name)
- \g,
- \g
- \g
+ _without_accessor(?:
+ \g # rb_struct_define_without_accessor(name, parent_name, ...)
+ |
+ _under\g # rb_struct_define_without_accessor_under(under, name, parent_name, ...)
+ \g,
+ )
+ \g,
+ \g,
+ \s*\w+, # Allocation function
)
- )
- |
- (?(?:\s*"\w+",)*\s*NULL\s*) {0}
- struct_define(?:
- \g # rb_struct_define(name, ...)
- \g,
- |
- _under\g # rb_struct_define_under(under, name, ...)
- \g,
- \g,
- |
- _without_accessor(?:
- \g # rb_struct_define_without_accessor(name, parent_name, ...)
+ \g
+ \g
|
- _under\g # rb_struct_define_without_accessor_under(under, name, parent_name, ...)
- \g,
- )
- \g,
- \g,
- \s*\w+, # Allocation function
- )
- \g
- \g
- |
- singleton_class\g # rb_singleton_class(target_class_name)
- (?\w+)
- \g
- )
- )mx
- ) do
- if target_class_name = $~[:target_class_name]
- # rb_singleton_class(target_class_name)
- handle_singleton $~[:var_name], target_class_name
- next
+ singleton_class\g # rb_singleton_class(target_class_name)
+ (?\w+)
+ \g
+ )
+ )mx
+ ) do
+ if target_class_name = $~[:target_class_name]
+ # rb_singleton_class(target_class_name)
+ handle_singleton $~[:var_name], target_class_name
+ next
+ end
+
+ var_name = $~[:var_name]
+ type = $~[:module] ? :module : :class
+ class_name = $~[:class_name]
+ parent_name = $~[:parent_name] || $~[:path]
+ under = $~[:under]
+ attributes = $~[:attributes]
+
+ handle_class_module(var_name, type, class_name, parent_name, under)
+ if attributes and !parent_name # rb_struct_define *not* without_accessor
+ true_flag = 'Qtrue'
+ attributes.scan(/"\K\w+(?=")/) do |attr_name|
+ handle_attr var_name, attr_name, true_flag, true_flag
+ end
+ end
+ end
end
- var_name = $~[:var_name]
- type = $~[:module] ? :module : :class
- class_name = $~[:class_name]
- parent_name = $~[:parent_name] || $~[:path]
- under = $~[:under]
- attributes = $~[:attributes]
-
- handle_class_module(var_name, type, class_name, parent_name, under)
- if attributes and !parent_name # rb_struct_define *not* without_accessor
- true_flag = 'Qtrue'
- attributes.scan(/"\K\w+(?=")/) do |attr_name|
- handle_attr var_name, attr_name, true_flag, true_flag
+ ##
+ # Scans #content for rb_define_variable, rb_define_readonly_variable,
+ # rb_define_const and rb_define_global_const
+
+ def do_constants
+ @content.scan(%r%\Wrb_define_
+ ( variable |
+ readonly_variable |
+ const |
+ global_const )
+ \s*\(
+ (?:\s*(\w+),)?
+ \s*"(\w+)",
+ \s*(.*?)\s*\)\s*;
+ %xm) do |type, var_name, const_name, definition|
+ var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
+ type = "const" if type == "global_const"
+ handle_constants type, var_name, const_name, definition
end
- end
- end
- end
- ##
- # Scans #content for rb_define_variable, rb_define_readonly_variable,
- # rb_define_const and rb_define_global_const
-
- def do_constants
- @content.scan(%r%\Wrb_define_
- ( variable |
- readonly_variable |
- const |
- global_const )
- \s*\(
- (?:\s*(\w+),)?
- \s*"(\w+)",
- \s*(.*?)\s*\)\s*;
- %xm) do |type, var_name, const_name, definition|
- var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
- type = "const" if type == "global_const"
- handle_constants type, var_name, const_name, definition
- end
+ @content.scan(%r%
+ \Wrb_curses_define_const
+ \s*\(
+ \s*
+ (\w+)
+ \s*
+ \)
+ \s*;%xm) do |consts|
+ const = consts.first
+
+ handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})"
+ end
- @content.scan(%r%
- \Wrb_curses_define_const
- \s*\(
- \s*
- (\w+)
- \s*
- \)
- \s*;%xm) do |consts|
- const = consts.first
-
- handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})"
- end
+ @content.scan(%r%
+ \Wrb_file_const
+ \s*\(
+ \s*
+ "([^"]+)",
+ \s*
+ (.*?)
+ \s*
+ \)
+ \s*;%xm) do |name, value|
+ handle_constants 'const', 'rb_mFConst', name, value
+ end
+ end
- @content.scan(%r%
- \Wrb_file_const
- \s*\(
- \s*
- "([^"]+)",
- \s*
- (.*?)
- \s*
- \)
- \s*;%xm) do |name, value|
- handle_constants 'const', 'rb_mFConst', name, value
- end
- end
+ ##
+ # Scans #content for rb_include_module
- ##
- # Scans #content for rb_include_module
+ def do_includes
+ @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c, m|
+ next unless cls = @classes[c]
+ m = @known_classes[m] || m
- def do_includes
- @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c, m|
- next unless cls = @classes[c]
- m = @known_classes[m] || m
+ comment = new_comment '', @top_level, :c
+ incl = cls.add_include Include.new(m, comment)
+ incl.record_location @top_level
+ end
+ end
- comment = new_comment '', @top_level, :c
- incl = cls.add_include RDoc::Include.new(m, comment)
- incl.record_location @top_level
- end
- end
+ ##
+ # Scans #content for rb_define_method, rb_define_singleton_method,
+ # rb_define_module_function, rb_define_private_method,
+ # rb_define_global_function and define_filetest_function
+
+ def do_methods
+ @content.scan(%r%rb_define_
+ (
+ singleton_method |
+ method |
+ module_function |
+ private_method
+ )
+ \s*\(\s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\(|\(METHOD\))?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))?
+ %xm) do |type, var_name, meth_name, function, param_count, source_file|
+
+ # Ignore top-object and weird struct.c dynamic stuff
+ next if var_name == "ruby_top_self"
+ next if var_name == "nstr"
+
+ var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_method(type, var_name, meth_name, function, param_count,
+ source_file)
+ end
- ##
- # Scans #content for rb_define_method, rb_define_singleton_method,
- # rb_define_module_function, rb_define_private_method,
- # rb_define_global_function and define_filetest_function
-
- def do_methods
- @content.scan(%r%rb_define_
- (
- singleton_method |
- method |
- module_function |
- private_method
- )
- \s*\(\s*([\w\.]+),
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\(|\(METHOD\))?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))?
- %xm) do |type, var_name, meth_name, function, param_count, source_file|
-
- # Ignore top-object and weird struct.c dynamic stuff
- next if var_name == "ruby_top_self"
- next if var_name == "nstr"
-
- var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_method(type, var_name, meth_name, function, param_count,
- source_file)
- end
+ @content.scan(%r%rb_define_global_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ %xm) do |meth_name, function, param_count, source_file|
+ handle_method("method", "rb_mKernel", meth_name, function, param_count,
+ source_file)
+ end
- @content.scan(%r%rb_define_global_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- %xm) do |meth_name, function, param_count, source_file|
- handle_method("method", "rb_mKernel", meth_name, function, param_count,
- source_file)
- end
+ @content.scan(/define_filetest_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count|
- @content.scan(/define_filetest_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count|
+ handle_method("method", "rb_mFileTest", meth_name, function, param_count)
+ handle_method("singleton_method", "rb_cFile", meth_name, function,
+ param_count)
+ end
+ end
- handle_method("method", "rb_mFileTest", meth_name, function, param_count)
- handle_method("singleton_method", "rb_cFile", meth_name, function,
- param_count)
- end
- end
+ ##
+ # Creates classes and module that were missing were defined due to the file
+ # order being different than the declaration order.
- ##
- # Creates classes and module that were missing were defined due to the file
- # order being different than the declaration order.
+ def do_missing
+ return if @missing_dependencies.empty?
- def do_missing
- return if @missing_dependencies.empty?
+ @enclosure_dependencies.tsort.each do |in_module|
+ arguments = @missing_dependencies.delete in_module
- @enclosure_dependencies.tsort.each do |in_module|
- arguments = @missing_dependencies.delete in_module
+ next unless arguments # dependency on existing class
- next unless arguments # dependency on existing class
+ handle_class_module(*arguments)
+ end
+ end
- handle_class_module(*arguments)
- end
- end
+ ##
+ # Finds the comment for an alias on +class_name+ from +new_name+ to
+ # +old_name+
- ##
- # Finds the comment for an alias on +class_name+ from +new_name+ to
- # +old_name+
+ def find_alias_comment(class_name, new_name, old_name)
+ content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
+ \s*"#{Regexp.escape new_name}"\s*,
+ \s*"#{Regexp.escape old_name}"\s*\);%xm
- def find_alias_comment(class_name, new_name, old_name)
- content =~ %r%((?>/\*.*?\*/\s+))
- rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
- \s*"#{Regexp.escape new_name}"\s*,
- \s*"#{Regexp.escape old_name}"\s*\);%xm
+ new_comment($1 || '', @top_level, :c)
+ end
- new_comment($1 || '', @top_level, :c)
- end
+ ##
+ # Finds a comment for rb_define_attr, rb_attr or Document-attr.
+ #
+ # +var_name+ is the C class variable the attribute is defined on.
+ # +attr_name+ is the attribute's name.
+ #
+ # +read+ and +write+ are the read/write flags ('1' or '0'). Either both or
+ # neither must be provided.
+
+ def find_attr_comment(var_name, attr_name, read = nil, write = nil)
+ attr_name = Regexp.escape attr_name
+
+ rw = if read and write
+ /\s*#{read}\s*,\s*#{write}\s*/xm
+ else
+ /.*?/m
+ end
+
+ comment = if @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*#{var_name},)?\s*
+ "#{attr_name}"\s*,
+ #{rw}\)\s*;%xm
+ $1
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_attr\(\s*#{var_name}\s*,
+ \s*#{attr_name}\s*,
+ #{rw},.*?\)\s*;%xm
+ $1
+ elsif @content =~ %r%(/\*.*?(?:\s*\*\s*)?)
+ Document-attr:\s#{attr_name}\s*?\n
+ ((?>(.|\n)*?\*/))%x
+ "#{$1}\n#{$2}"
+ else
+ ''
+ end
- ##
- # Finds a comment for rb_define_attr, rb_attr or Document-attr.
- #
- # +var_name+ is the C class variable the attribute is defined on.
- # +attr_name+ is the attribute's name.
- #
- # +read+ and +write+ are the read/write flags ('1' or '0'). Either both or
- # neither must be provided.
-
- def find_attr_comment(var_name, attr_name, read = nil, write = nil)
- attr_name = Regexp.escape attr_name
-
- rw = if read and write
- /\s*#{read}\s*,\s*#{write}\s*/xm
- else
- /.*?/m
- end
-
- comment = if @content =~ %r%((?>/\*.*?\*/\s+))
- rb_define_attr\((?:\s*#{var_name},)?\s*
- "#{attr_name}"\s*,
- #{rw}\)\s*;%xm
- $1
- elsif @content =~ %r%((?>/\*.*?\*/\s+))
- rb_attr\(\s*#{var_name}\s*,
- \s*#{attr_name}\s*,
- #{rw},.*?\)\s*;%xm
- $1
- elsif @content =~ %r%(/\*.*?(?:\s*\*\s*)?)
- Document-attr:\s#{attr_name}\s*?\n
- ((?>(.|\n)*?\*/))%x
- "#{$1}\n#{$2}"
- else
- ''
- end
-
- new_comment comment, @top_level, :c
- end
+ new_comment comment, @top_level, :c
+ end
- ##
- # Generate a Ruby-method table
-
- def gen_body_table(file_content)
- table = {}
- file_content.scan(%r{
- ((?>/\*.*?\*/\s*)?)
- ((?:\w+\s+){0,2} VALUE\s+(\w+)
- \s*(?:\([^\)]*\))(?:[^\);]|$))
- | ((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+(\w+)\s+(\w+))
- | ^\s*\#\s*define\s+(\w+)\s+(\w+)
- }xm) do
- case
- when name = $3
- table[name] = [:func_def, $1, $2, $~.offset(2)] if !(t = table[name]) || t[0] != :func_def
- when name = $6
- table[name] = [:macro_def, $4, $5, $~.offset(5), $7] if !(t = table[name]) || t[0] == :macro_alias
- when name = $8
- table[name] ||= [:macro_alias, $9]
+ ##
+ # Generate a Ruby-method table
+
+ def gen_body_table(file_content)
+ table = {}
+ file_content.scan(%r{
+ ((?>/\*.*?\*/\s*)?)
+ ((?:\w+\s+){0,2} VALUE\s+(\w+)
+ \s*(?:\([^\)]*\))(?:[^\);]|$))
+ | ((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+(\w+)\s+(\w+))
+ | ^\s*\#\s*define\s+(\w+)\s+(\w+)
+ }xm) do
+ case
+ when name = $3
+ table[name] = [:func_def, $1, $2, $~.offset(2)] if !(t = table[name]) || t[0] != :func_def
+ when name = $6
+ table[name] = [:macro_def, $4, $5, $~.offset(5), $7] if !(t = table[name]) || t[0] == :macro_alias
+ when name = $8
+ table[name] ||= [:macro_alias, $9]
+ end
+ end
+ table
end
- end
- table
- end
- ##
- # Find the C code corresponding to a Ruby method
+ ##
+ # Find the C code corresponding to a Ruby method
- def find_body(class_name, meth_name, meth_obj, file_content, quiet = false)
- if file_content
- @body_table ||= {}
- @body_table[file_content] ||= gen_body_table file_content
- type, *args = @body_table[file_content][meth_name]
- end
+ def find_body(class_name, meth_name, meth_obj, file_content, quiet = false)
+ if file_content
+ @body_table ||= {}
+ @body_table[file_content] ||= gen_body_table file_content
+ type, *args = @body_table[file_content][meth_name]
+ end
- case type
- when :func_def
- comment = new_comment args[0], @top_level, :c
- body = args[1]
- offset, = args[2]
+ case type
+ when :func_def
+ comment = new_comment args[0], @top_level, :c
+ body = args[1]
+ offset, = args[2]
- # try to find the whole body
- body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content
+ # try to find the whole body
+ body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content
- # The comment block may have been overridden with a 'Document-method'
- # block. This happens in the interpreter when multiple methods are
- # vectored through to the same C method but those methods are logically
- # distinct (for example Kernel.hash and Kernel.object_id share the same
- # implementation
+ # The comment block may have been overridden with a 'Document-method'
+ # block. This happens in the interpreter when multiple methods are
+ # vectored through to the same C method but those methods are logically
+ # distinct (for example Kernel.hash and Kernel.object_id share the same
+ # implementation
- override_comment = find_override_comment class_name, meth_obj
- comment = override_comment if override_comment
+ override_comment = find_override_comment class_name, meth_obj
+ comment = override_comment if override_comment
- find_modifiers comment, meth_obj if comment
+ find_modifiers comment, meth_obj if comment
- #meth_obj.params = params
- meth_obj.start_collecting_tokens(:c)
- tk = { :line_no => 1, :char_no => 1, :text => body }
- meth_obj.add_token tk
- meth_obj.comment = comment
- meth_obj.line = file_content[0, offset].count("\n") + 1
+ #meth_obj.params = params
+ meth_obj.start_collecting_tokens(:c)
+ tk = { :line_no => 1, :char_no => 1, :text => body }
+ meth_obj.add_token tk
+ meth_obj.comment = comment
+ meth_obj.line = file_content[0, offset].count("\n") + 1
- body
- when :macro_def
- comment = new_comment args[0], @top_level, :c
- body = args[1]
- offset, = args[2]
+ body
+ when :macro_def
+ comment = new_comment args[0], @top_level, :c
+ body = args[1]
+ offset, = args[2]
- find_body class_name, args[3], meth_obj, file_content, true
+ find_body class_name, args[3], meth_obj, file_content, true
- find_modifiers comment, meth_obj
+ find_modifiers comment, meth_obj
- meth_obj.start_collecting_tokens(:c)
- tk = { :line_no => 1, :char_no => 1, :text => body }
- meth_obj.add_token tk
- meth_obj.comment = comment
- meth_obj.line = file_content[0, offset].count("\n") + 1
+ meth_obj.start_collecting_tokens(:c)
+ tk = { :line_no => 1, :char_no => 1, :text => body }
+ meth_obj.add_token tk
+ meth_obj.comment = comment
+ meth_obj.line = file_content[0, offset].count("\n") + 1
- body
- when :macro_alias
- # with no comment we hope the aliased definition has it and use it's
- # definition
+ body
+ when :macro_alias
+ # with no comment we hope the aliased definition has it and use it's
+ # definition
- body = find_body(class_name, args[0], meth_obj, file_content, true)
+ body = find_body(class_name, args[0], meth_obj, file_content, true)
- return body if body
+ return body if body
- @options.warn "No definition for #{meth_name}"
- false
- else # No body, but might still have an override comment
- comment = find_override_comment class_name, meth_obj
+ @options.warn "No definition for #{meth_name}"
+ false
+ else # No body, but might still have an override comment
+ comment = find_override_comment class_name, meth_obj
- if comment
- find_modifiers comment, meth_obj
- meth_obj.comment = comment
+ if comment
+ find_modifiers comment, meth_obj
+ meth_obj.comment = comment
- ''
- else
- @options.warn "No definition for #{meth_name}"
- false
+ ''
+ else
+ @options.warn "No definition for #{meth_name}"
+ false
+ end
+ end
end
- end
- end
-
- ##
- # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+
- def find_class(raw_name, name, base_name = nil)
- unless @classes[raw_name]
- if raw_name =~ /^rb_m/
- container = @top_level.add_module RDoc::NormalModule, name
- else
- container = @top_level.add_class RDoc::NormalClass, name
+ ##
+ # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+
+
+ def find_class(raw_name, name, base_name = nil)
+ unless @classes[raw_name]
+ if raw_name =~ /^rb_m/
+ container = @top_level.add_module NormalModule, name
+ else
+ container = @top_level.add_class NormalClass, name
+ end
+ container.name = base_name if base_name
+
+ container.record_location @top_level
+ @top_level.add_to_classes_or_modules container
+ @classes[raw_name] = container
+ end
+ @classes[raw_name]
end
- container.name = base_name if base_name
-
- container.record_location @top_level
- @top_level.add_to_classes_or_modules container
- @classes[raw_name] = container
- end
- @classes[raw_name]
- end
-
- ##
- # Look for class or module documentation above Init_+class_name+(void),
- # in a Document-class +class_name+ (or module) comment or above an
- # rb_define_class (or module). If a comment is supplied above a matching
- # Init_ and a rb_define_class the Init_ comment is used.
- #
- # /*
- # * This is a comment for Foo
- # */
- # Init_Foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * Document-class: Foo
- # * This is a comment for Foo
- # */
- # Init_foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * This is a comment for Foo
- # */
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
-
- def find_class_comment(class_name, class_mod)
- comment = nil
-
- if @content =~ %r%
- ((?>/\*.*?\*/\s+))
- (static\s+)?
- void\s+
- Init(?:VM)?_(?i:#{class_name})\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xm
- comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
- elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
- (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm
- comment = "/*\n#{$1}"
- elsif @content =~ %r%((?>/\*.*?\*/\s+))
- ([\w\.\s]+\s* = \s+)?rb_define_(class|module)[\t (]*?"(#{class_name})"%xm
- comment = $1
- elsif @content =~ %r%((?>/\*.*?\*/\s+))
- ([\w\. \t]+ = \s+)?rb_define_(class|module)_under[\t\w, (]*?"(#{class_name.split('::').last})"%xm
- comment = $1
- else
- comment = ''
- end
- comment = new_comment comment, @top_level, :c
+ ##
+ # Look for class or module documentation above Init_+class_name+(void),
+ # in a Document-class +class_name+ (or module) comment or above an
+ # rb_define_class (or module). If a comment is supplied above a matching
+ # Init_ and a rb_define_class the Init_ comment is used.
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # Init_Foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * Document-class: Foo
+ # * This is a comment for Foo
+ # */
+ # Init_foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+
+ def find_class_comment(class_name, class_mod)
+ comment = nil
+
+ if @content =~ %r%
+ ((?>/\*.*?\*/\s+))
+ (static\s+)?
+ void\s+
+ Init(?:VM)?_(?i:#{class_name})\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xm
+ comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
+ elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
+ (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm
+ comment = "/*\n#{$1}"
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ ([\w\.\s]+\s* = \s+)?rb_define_(class|module)[\t (]*?"(#{class_name})"%xm
+ comment = $1
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ ([\w\. \t]+ = \s+)?rb_define_(class|module)_under[\t\w, (]*?"(#{class_name.split('::').last})"%xm
+ comment = $1
+ else
+ comment = ''
+ end
- look_for_directives_in class_mod, comment
+ comment = new_comment comment, @top_level, :c
- class_mod.add_comment comment, @top_level
- end
+ look_for_directives_in class_mod, comment
- ##
- # Generate a const table
-
- def gen_const_table(file_content)
- table = {}
- @content.scan(%r{
- (?(?>^\s*/\*.*?\*/\s+))
- rb_define_(?\w+)\(\s*(?:\w+),\s*
- "(?\w+)"\s*,
- .*?\)\s*;
- | (?(?>^\s*/\*.*?\*/\s+))
- rb_define_global_(?const)\(\s*
- "(?\w+)"\s*,
- .*?\)\s*;
- | (?(?>^\s*/\*.*?\*/\s+))
- rb_file_(?const)\(\s*
- "(?\w+)"\s*,
- .*?\)\s*;
- | (?(?>^\s*/\*.*?\*/\s+))
- rb_curses_define_(?const)\(\s*
- (?\w+)
- \s*\)\s*;
- | Document-(?:const|global|variable):\s
- (?(?:\w+::)*\w+)
- \s*?\n(?(?>.*?\*/))
- }mxi) do
- name, doc, type = $~.values_at(:name, :doc, :type)
- if type
- table[[type, name]] = doc
- else
- table[name] = "/*\n" + doc
+ class_mod.add_comment comment, @top_level
end
- end
- table
- end
-
- ##
- # Finds a comment matching +type+ and +const_name+ either above the
- # comment or in the matching Document- section.
-
- def find_const_comment(type, const_name, class_name = nil)
- @const_table ||= {}
- @const_table[@content] ||= gen_const_table @content
- table = @const_table[@content]
-
- comment =
- table[[type, const_name]] ||
- (class_name && table[class_name + "::" + const_name]) ||
- table[const_name] ||
- ''
-
- new_comment comment, @top_level, :c
- end
- ##
- # Handles modifiers in +comment+ and updates +meth_obj+ as appropriate.
-
- def find_modifiers(comment, meth_obj)
- look_for_directives_in meth_obj, comment
- end
-
- ##
- # Finds a Document-method override for +meth_obj+ on +class_name+
+ ##
+ # Generate a const table
+
+ def gen_const_table(file_content)
+ table = {}
+ @content.scan(%r{
+ (?(?>^\s*/\*.*?\*/\s+))
+ rb_define_(?\w+)\(\s*(?:\w+),\s*
+ "(?\w+)"\s*,
+ .*?\)\s*;
+ | (?(?>^\s*/\*.*?\*/\s+))
+ rb_define_global_(?const)\(\s*
+ "(?\w+)"\s*,
+ .*?\)\s*;
+ | (?(?>^\s*/\*.*?\*/\s+))
+ rb_file_(?const)\(\s*
+ "(?\w+)"\s*,
+ .*?\)\s*;
+ | (?(?>^\s*/\*.*?\*/\s+))
+ rb_curses_define_(?const)\(\s*
+ (?\w+)
+ \s*\)\s*;
+ | Document-(?:const|global|variable):\s
+ (?(?:\w+::)*\w+)
+ \s*?\n(?(?>.*?\*/))
+ }mxi) do
+ name, doc, type = $~.values_at(:name, :doc, :type)
+ if type
+ table[[type, name]] = doc
+ else
+ table[name] = "/*\n" + doc
+ end
+ end
+ table
+ end
- def find_override_comment(class_name, meth_obj)
- name = Regexp.escape meth_obj.name
- prefix = Regexp.escape meth_obj.name_prefix
+ ##
+ # Finds a comment matching +type+ and +const_name+ either above the
+ # comment or in the matching Document- section.
- comment = if @content =~ %r%Document-method:
- \s+#{class_name}#{prefix}#{name}
- \s*?\n((?>.*?\*/))%xm
- "/*\n#{$1}"
- elsif @content =~ %r%Document-method:
- \s#{name}\s*?\n((?>.*?\*/))%xm
- "/*\n#{$1}"
- end
+ def find_const_comment(type, const_name, class_name = nil)
+ @const_table ||= {}
+ @const_table[@content] ||= gen_const_table @content
+ table = @const_table[@content]
- return unless comment
+ comment =
+ table[[type, const_name]] ||
+ (class_name && table[class_name + "::" + const_name]) ||
+ table[const_name] ||
+ ''
- new_comment comment, @top_level, :c
- end
-
- ##
- # Creates a new RDoc::Attr +attr_name+ on class +var_name+ that is either
- # +read+, +write+ or both
+ new_comment comment, @top_level, :c
+ end
- def handle_attr(var_name, attr_name, read, write)
- rw = ''
- rw += 'R' if TRUE_VALUES.include?(read)
- rw += 'W' if TRUE_VALUES.include?(write)
+ ##
+ # Handles modifiers in +comment+ and updates +meth_obj+ as appropriate.
- class_name = @known_classes[var_name]
+ def find_modifiers(comment, meth_obj)
+ look_for_directives_in meth_obj, comment
+ end
- return unless class_name
+ ##
+ # Finds a Document-method override for +meth_obj+ on +class_name+
- class_obj = find_class var_name, class_name
+ def find_override_comment(class_name, meth_obj)
+ name = Regexp.escape meth_obj.name
+ prefix = Regexp.escape meth_obj.name_prefix
- return unless class_obj
+ comment = if @content =~ %r%Document-method:
+ \s+#{class_name}#{prefix}#{name}
+ \s*?\n((?>.*?\*/))%xm
+ "/*\n#{$1}"
+ elsif @content =~ %r%Document-method:
+ \s#{name}\s*?\n((?>.*?\*/))%xm
+ "/*\n#{$1}"
+ end
- comment = find_attr_comment var_name, attr_name
- comment.normalize
+ return unless comment
- name = attr_name.gsub(/rb_intern(?:_const)?\("([^"]+)"\)/, '\1')
+ new_comment comment, @top_level, :c
+ end
- attr = RDoc::Attr.new name, rw, comment
+ ##
+ # Creates a new RDoc::Attr +attr_name+ on class +var_name+ that is either
+ # +read+, +write+ or both
- attr.record_location @top_level
- class_obj.add_attribute attr
- @stats.add_attribute attr
- end
+ def handle_attr(var_name, attr_name, read, write)
+ rw = ''
+ rw += 'R' if TRUE_VALUES.include?(read)
+ rw += 'W' if TRUE_VALUES.include?(write)
- ##
- # Creates a new RDoc::NormalClass or RDoc::NormalModule based on +type+
- # named +class_name+ in +parent+ which was assigned to the C +var_name+.
+ class_name = @known_classes[var_name]
- def handle_class_module(var_name, type, class_name, parent, in_module)
- parent_name = @known_classes[parent] || parent
+ return unless class_name
- if in_module
- enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
+ class_obj = find_class var_name, class_name
- if enclosure.nil? and enclosure = @known_classes[in_module]
- enc_type = /^rb_m/ =~ in_module ? :module : :class
- handle_class_module in_module, enc_type, enclosure, nil, nil
- enclosure = @classes[in_module]
- end
+ return unless class_obj
- unless enclosure
- @enclosure_dependencies[in_module] << var_name
- @missing_dependencies[var_name] =
- [var_name, type, class_name, parent, in_module]
+ comment = find_attr_comment var_name, attr_name
+ comment.normalize
- return
- end
- else
- enclosure = @top_level
- end
+ name = attr_name.gsub(/rb_intern(?:_const)?\("([^"]+)"\)/, '\1')
- if type == :class
- full_name = if RDoc::ClassModule === enclosure
- enclosure.full_name + "::#{class_name}"
- else
- class_name
- end
+ attr = Attr.new name, rw, comment
- if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)%
- parent_name = $1
+ attr.record_location @top_level
+ class_obj.add_attribute attr
+ @stats.add_attribute attr
end
- cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
- else
- cm = enclosure.add_module RDoc::NormalModule, class_name
- end
+ ##
+ # Creates a new RDoc::NormalClass or RDoc::NormalModule based on +type+
+ # named +class_name+ in +parent+ which was assigned to the C +var_name+.
- cm.record_location enclosure.top_level
- enclosure.top_level.add_to_classes_or_modules cm
+ def handle_class_module(var_name, type, class_name, parent, in_module)
+ parent_name = @known_classes[parent] || parent
- find_class_comment cm.full_name, cm
+ if in_module
+ enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
- case cm
- when RDoc::NormalClass
- @stats.add_class cm
- when RDoc::NormalModule
- @stats.add_module cm
- end
+ if enclosure.nil? and enclosure = @known_classes[in_module]
+ enc_type = /^rb_m/ =~ in_module ? :module : :class
+ handle_class_module in_module, enc_type, enclosure, nil, nil
+ enclosure = @classes[in_module]
+ end
- @classes[var_name] = cm
- @known_classes[var_name] = cm.full_name
- @store.add_c_enclosure var_name, cm
- end
+ unless enclosure
+ @enclosure_dependencies[in_module] << var_name
+ @missing_dependencies[var_name] =
+ [var_name, type, class_name, parent, in_module]
- ##
- # Adds constants. By providing some_value: at the start of the comment you
- # can override the C value of the comment to give a friendly definition.
- #
- # /* 300: The perfect score in bowling */
- # rb_define_const(cFoo, "PERFECT", INT2FIX(300));
- #
- # Will override INT2FIX(300) with the value +300+ in the output
- # RDoc. Values may include quotes and escaped colons (\:).
-
- def handle_constants(type, var_name, const_name, definition)
- class_name = @known_classes[var_name]
+ return
+ end
+ else
+ enclosure = @top_level
+ end
- return unless class_name
+ if type == :class
+ full_name = if ClassModule === enclosure
+ enclosure.full_name + "::#{class_name}"
+ else
+ class_name
+ end
- class_obj = find_class var_name, class_name, class_name[/::\K[^:]+\z/]
+ if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)%
+ parent_name = $1
+ end
- unless class_obj
- @options.warn 'Enclosing class or module %p is not known' % [const_name]
- return
- end
+ cm = enclosure.add_class NormalClass, class_name, parent_name
+ else
+ cm = enclosure.add_module NormalModule, class_name
+ end
- comment = find_const_comment type, const_name, class_name
- comment.normalize
+ cm.record_location enclosure.top_level
+ enclosure.top_level.add_to_classes_or_modules cm
- # In the case of rb_define_const, the definition and comment are in
- # "/* definition: comment */" form. The literal ':' and '\' characters
- # can be escaped with a backslash.
- if type.downcase == 'const'
- if /\A(.+?)?:(?!\S)/ =~ comment.text
- new_definition, new_comment = $1, $'
+ find_class_comment cm.full_name, cm
- if !new_definition # Default to literal C definition
- new_definition = definition
- else
- new_definition = new_definition.gsub(/\\([\\:])/, '\1')
+ case cm
+ when NormalClass
+ @stats.add_class cm
+ when NormalModule
+ @stats.add_module cm
end
- new_definition.sub!(/\A(\s+)/, '')
+ @classes[var_name] = cm
+ @known_classes[var_name] = cm.full_name
+ @store.add_c_enclosure var_name, cm
+ end
- new_comment = "#{$1}#{new_comment.lstrip}"
+ ##
+ # Adds constants. By providing some_value: at the start of the comment you
+ # can override the C value of the comment to give a friendly definition.
+ #
+ # /* 300: The perfect score in bowling */
+ # rb_define_const(cFoo, "PERFECT", INT2FIX(300));
+ #
+ # Will override INT2FIX(300) with the value +300+ in the output
+ # RDoc. Values may include quotes and escaped colons (\:).
- new_comment = self.new_comment(new_comment, @top_level, :c)
+ def handle_constants(type, var_name, const_name, definition)
+ class_name = @known_classes[var_name]
- con = RDoc::Constant.new const_name, new_definition, new_comment
- else
- con = RDoc::Constant.new const_name, definition, comment
- end
- else
- con = RDoc::Constant.new const_name, definition, comment
- end
+ return unless class_name
- con.record_location @top_level
- @stats.add_constant con
- class_obj.add_constant con
- end
+ class_obj = find_class var_name, class_name, class_name[/::\K[^:]+\z/]
- ##
- # Removes #ifdefs that would otherwise confuse us
+ unless class_obj
+ @options.warn 'Enclosing class or module %p is not known' % [const_name]
+ return
+ end
- def handle_ifdefs_in(body)
- body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
- end
+ comment = find_const_comment type, const_name, class_name
+ comment.normalize
- ##
- # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned
- # to +var_name+. +type+ is the type of method definition function used.
- # +singleton_method+ and +module_function+ create a singleton method.
+ # In the case of rb_define_const, the definition and comment are in
+ # "/* definition: comment */" form. The literal ':' and '\' characters
+ # can be escaped with a backslash.
+ if type.downcase == 'const'
+ if /\A(.+?)?:(?!\S)/ =~ comment.text
+ new_definition, new_comment = $1, $'
- def handle_method(type, var_name, meth_name, function, param_count,
- source_file = nil)
- class_name = @known_classes[var_name]
- singleton = @singleton_classes.key?(var_name) || %w[singleton_method module_function].include?(type)
+ if !new_definition # Default to literal C definition
+ new_definition = definition
+ else
+ new_definition = new_definition.gsub(/\\([\\:])/, '\1')
+ end
- @methods[var_name][function] << meth_name
+ new_definition.sub!(/\A(\s+)/, '')
- return unless class_name
+ new_comment = "#{$1}#{new_comment.lstrip}"
- class_obj = find_class var_name, class_name
+ new_comment = self.new_comment(new_comment, @top_level, :c)
- if existing_method = class_obj.method_list.find { |m| m.c_function == function && m.singleton == singleton }
- add_alias(var_name, class_obj, existing_method.name, meth_name, existing_method.comment, singleton: singleton)
- end
+ con = Constant.new const_name, new_definition, new_comment
+ else
+ con = Constant.new const_name, definition, comment
+ end
+ else
+ con = Constant.new const_name, definition, comment
+ end
- if class_obj
- if meth_name == 'initialize'
- meth_name = 'new'
- singleton = true
- type = 'method' # force public
+ con.record_location @top_level
+ @stats.add_constant con
+ class_obj.add_constant con
end
- meth_obj = RDoc::AnyMethod.new meth_name, singleton: singleton
- meth_obj.c_function = function
+ ##
+ # Removes #ifdefs that would otherwise confuse us
- p_count = Integer(param_count) rescue -1
+ def handle_ifdefs_in(body)
+ body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
+ end
- if source_file
- file_name = File.join @file_dir, source_file
+ ##
+ # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned
+ # to +var_name+. +type+ is the type of method definition function used.
+ # +singleton_method+ and +module_function+ create a singleton method.
- if File.exist? file_name
- file_content = RDoc::Encoding.read_file file_name, @options.encoding
- else
- @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}"
- end
- else
- file_content = @content
- end
+ def handle_method(type, var_name, meth_name, function, param_count,
+ source_file = nil)
+ class_name = @known_classes[var_name]
+ singleton = @singleton_classes.key?(var_name) || %w[singleton_method module_function].include?(type)
- body = find_body class_name, function, meth_obj, file_content
+ @methods[var_name][function] << meth_name
- if body and meth_obj.document_self
- meth_obj.params = if p_count < -1 # -2 is Array
- '(*args)'
- elsif p_count == -1 # argc, argv
- rb_scan_args body
- else
- args = (1..p_count).map { |i| "p#{i}" }
- "(#{args.join ', '})"
- end
+ return unless class_name
+ class_obj = find_class var_name, class_name
- meth_obj.record_location @top_level
+ if existing_method = class_obj.method_list.find { |m| m.c_function == function && m.singleton == singleton }
+ add_alias(var_name, class_obj, existing_method.name, meth_name, existing_method.comment, singleton: singleton)
+ end
- if meth_obj.section_title
- class_obj.temporary_section = class_obj.add_section(meth_obj.section_title)
+ if class_obj
+ if meth_name == 'initialize'
+ meth_name = 'new'
+ singleton = true
+ type = 'method' # force public
+ end
+
+ meth_obj = AnyMethod.new meth_name, singleton: singleton
+ meth_obj.c_function = function
+
+ p_count = Integer(param_count) rescue -1
+
+ if source_file
+ file_name = File.join @file_dir, source_file
+
+ if File.exist? file_name
+ file_content = Encoding.read_file file_name, @options.encoding
+ else
+ @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}"
+ end
+ else
+ file_content = @content
+ end
+
+ body = find_body class_name, function, meth_obj, file_content
+
+ if body and meth_obj.document_self
+ meth_obj.params = if p_count < -1 # -2 is Array
+ '(*args)'
+ elsif p_count == -1 # argc, argv
+ rb_scan_args body
+ else
+ args = (1..p_count).map { |i| "p#{i}" }
+ "(#{args.join ', '})"
+ end
+
+
+ meth_obj.record_location @top_level
+
+ if meth_obj.section_title
+ class_obj.temporary_section = class_obj.add_section(meth_obj.section_title)
+ end
+ meth_obj.visibility = type == 'private_method' ? :private : :public
+ class_obj.add_method meth_obj
+ @stats.add_method meth_obj
+ end
end
- meth_obj.visibility = type == 'private_method' ? :private : :public
- class_obj.add_method meth_obj
- @stats.add_method meth_obj
end
- end
- end
- ##
- # Registers a singleton class +sclass_var+ as a singleton of +class_var+
+ ##
+ # Registers a singleton class +sclass_var+ as a singleton of +class_var+
- def handle_singleton(sclass_var, class_var)
- if (klass = @classes[class_var])
- @classes[sclass_var] = klass
- end
- if (class_name = @known_classes[class_var])
- @known_classes[sclass_var] = class_name
- @singleton_classes[sclass_var] = class_name
- end
- end
+ def handle_singleton(sclass_var, class_var)
+ if (klass = @classes[class_var])
+ @classes[sclass_var] = klass
+ end
+ if (class_name = @known_classes[class_var])
+ @known_classes[sclass_var] = class_name
+ @singleton_classes[sclass_var] = class_name
+ end
+ end
- ##
- # Loads the variable map with the given +name+ from the RDoc::Store, if
- # present.
+ ##
+ # Loads the variable map with the given +name+ from the RDoc::Store, if
+ # present.
- def load_variable_map(map_name)
- return {} unless files = @store.cache[map_name]
- return {} unless name_map = files[@file_name]
+ def load_variable_map(map_name)
+ return {} unless files = @store.cache[map_name]
+ return {} unless name_map = files[@file_name]
- class_map = {}
+ class_map = {}
- name_map.each do |variable, name|
- next unless mod = @store.find_class_or_module(name)
+ name_map.each do |variable, name|
+ next unless mod = @store.find_class_or_module(name)
- class_map[variable] = if map_name == :c_class_variables
- mod
- else
- name
- end
- @known_classes[variable] = name
- end
+ class_map[variable] = if map_name == :c_class_variables
+ mod
+ else
+ name
+ end
+ @known_classes[variable] = name
+ end
- class_map
- end
+ class_map
+ end
- ##
- # Look for directives in a normal comment block:
- #
- # /*
- # * :nodoc:
- # */
- #
- # This method modifies the +comment+
-
- def look_for_directives_in(context, comment)
- comment.text, format = @preprocess.run_pre_processes(comment.text, context, comment.line || 1, :c)
- comment.format = format if format
- @preprocess.run_post_processes(comment, context)
- comment.normalized = true
- comment
- end
+ ##
+ # Look for directives in a normal comment block:
+ #
+ # /*
+ # * :nodoc:
+ # */
+ #
+ # This method modifies the +comment+
+
+ def look_for_directives_in(context, comment)
+ comment.text, format = @preprocess.run_pre_processes(comment.text, context, comment.line || 1, :c)
+ comment.format = format if format
+ @preprocess.run_post_processes(comment, context)
+ comment.normalized = true
+ comment
+ end
- ##
- # Extracts parameters from the +method_body+ and returns a method
- # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT
+ ##
+ # Extracts parameters from the +method_body+ and returns a method
+ # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT
- def rb_scan_args(method_body)
- method_body =~ /rb_scan_args\((.*?)\)/m
- return '(*args)' unless $1
+ def rb_scan_args(method_body)
+ method_body =~ /rb_scan_args\((.*?)\)/m
+ return '(*args)' unless $1
- $1.split(/,/)[2] =~ /"(.*?)"/ # format argument
- format = $1.split(//)
+ $1.split(/,/)[2] =~ /"(.*?)"/ # format argument
+ format = $1.split(//)
- lead = opt = trail = 0
+ lead = opt = trail = 0
- if format.first =~ /\d/
- lead = $&.to_i
- format.shift
- if format.first =~ /\d/
- opt = $&.to_i
- format.shift
if format.first =~ /\d/
- trail = $&.to_i
+ lead = $&.to_i
format.shift
- block_arg = true
+ if format.first =~ /\d/
+ opt = $&.to_i
+ format.shift
+ if format.first =~ /\d/
+ trail = $&.to_i
+ format.shift
+ block_arg = true
+ end
+ end
end
- end
- end
- if format.first == '*' and not block_arg
- var = true
- format.shift
- if format.first =~ /\d/
- trail = $&.to_i
- format.shift
- end
- end
+ if format.first == '*' and not block_arg
+ var = true
+ format.shift
+ if format.first =~ /\d/
+ trail = $&.to_i
+ format.shift
+ end
+ end
- if format.first == ':'
- hash = true
- format.shift
- end
+ if format.first == ':'
+ hash = true
+ format.shift
+ end
- if format.first == '&'
- block = true
- format.shift
- end
+ if format.first == '&'
+ block = true
+ format.shift
+ end
- # if the format string is not empty there's a bug in the C code, ignore it
+ # if the format string is not empty there's a bug in the C code, ignore it
- args = []
- position = 1
+ args = []
+ position = 1
- (1...(position + lead)).each do |index|
- args << "p#{index}"
- end
+ (1...(position + lead)).each do |index|
+ args << "p#{index}"
+ end
- position += lead
+ position += lead
- (position...(position + opt)).each do |index|
- args << "p#{index} = v#{index}"
- end
+ (position...(position + opt)).each do |index|
+ args << "p#{index} = v#{index}"
+ end
- position += opt
+ position += opt
- if var
- args << '*args'
- position += 1
- end
+ if var
+ args << '*args'
+ position += 1
+ end
- (position...(position + trail)).each do |index|
- args << "p#{index}"
- end
+ (position...(position + trail)).each do |index|
+ args << "p#{index}"
+ end
- position += trail
+ position += trail
- if hash
- args << "p#{position} = {}"
- end
+ if hash
+ args << "p#{position} = {}"
+ end
- args << '&block' if block
+ args << '&block' if block
- "(#{args.join ', '})"
- end
+ "(#{args.join ', '})"
+ end
- ##
- # Removes lines that are commented out that might otherwise get picked up
- # when scanning for classes and methods
+ ##
+ # Removes lines that are commented out that might otherwise get picked up
+ # when scanning for classes and methods
- def remove_commented_out_lines
- @content = @content.gsub(%r%//.*rb_define_%, '//')
- end
+ def remove_commented_out_lines
+ @content = @content.gsub(%r%//.*rb_define_%, '//')
+ end
- ##
- # Extracts the classes, modules, methods, attributes, constants and aliases
- # from a C file and returns an RDoc::TopLevel for this file
+ ##
+ # Extracts the classes, modules, methods, attributes, constants and aliases
+ # from a C file and returns an RDoc::TopLevel for this file
- def scan
- remove_commented_out_lines
+ def scan
+ remove_commented_out_lines
- do_classes_and_modules
- do_missing
+ do_classes_and_modules
+ do_missing
- do_constants
- do_methods
- do_includes
- do_aliases
- do_attrs
+ do_constants
+ do_methods
+ do_includes
+ do_aliases
+ do_attrs
- @store.add_c_variables self
+ @store.add_c_variables self
- @top_level
- end
+ @top_level
+ end
- ##
- # Creates a RDoc::Comment instance.
+ ##
+ # Creates a RDoc::Comment instance.
- def new_comment(text = nil, location = nil, language = nil)
- RDoc::Comment.new(text, location, language).tap do |comment|
- comment.format = @markup
+ def new_comment(text = nil, location = nil, language = nil)
+ Comment.new(text, location, language).tap do |comment|
+ comment.format = @markup
+ end
+ end
end
end
end
diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb
index da79d7dd7e..7ecebbe8af 100644
--- a/lib/rdoc/parser/changelog.rb
+++ b/lib/rdoc/parser/changelog.rb
@@ -1,377 +1,381 @@
# frozen_string_literal: true
-##
-# A ChangeLog file parser.
-#
-# This parser converts a ChangeLog into an RDoc::Markup::Document. When
-# viewed as HTML a ChangeLog page will have an entry for each day's entries in
-# the sidebar table of contents.
-#
-# This parser is meant to parse the MRI ChangeLog, but can be used to parse any
-# {GNU style Change
-# Log}[http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html].
-
-class RDoc::Parser::ChangeLog < RDoc::Parser
-
- include RDoc::Parser::Text
-
- parse_files_matching(/(\/|\\|\A)ChangeLog[^\/\\]*\z/)
-
- ##
- # Attaches the +continuation+ of the previous line to the +entry_body+.
- #
- # Continued function listings are joined together as a single entry.
- # Continued descriptions are joined to make a single paragraph.
-
- def continue_entry_body(entry_body, continuation)
- return unless last = entry_body.last
-
- if last =~ /\)\s*\z/ and continuation =~ /\A\(/
- last.sub!(/\)\s*\z/, ',')
- continuation = continuation.sub(/\A\(/, '')
- end
-
- if last =~ /\s\z/
- last << continuation
- else
- last << ' ' + continuation
- end
- end
+module RDoc
+ class Parser
+ ##
+ # A ChangeLog file parser.
+ #
+ # This parser converts a ChangeLog into an RDoc::Markup::Document. When
+ # viewed as HTML a ChangeLog page will have an entry for each day's entries in
+ # the sidebar table of contents.
+ #
+ # This parser is meant to parse the MRI ChangeLog, but can be used to parse any
+ # {GNU style Change
+ # Log}[http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html].
+
+ class ChangeLog < Parser
+
+ include Parser::Text
+
+ parse_files_matching(/(\/|\\|\A)ChangeLog[^\/\\]*\z/)
+
+ ##
+ # Attaches the +continuation+ of the previous line to the +entry_body+.
+ #
+ # Continued function listings are joined together as a single entry.
+ # Continued descriptions are joined to make a single paragraph.
+
+ def continue_entry_body(entry_body, continuation)
+ return unless last = entry_body.last
+
+ if last =~ /\)\s*\z/ and continuation =~ /\A\(/
+ last.sub!(/\)\s*\z/, ',')
+ continuation = continuation.sub(/\A\(/, '')
+ end
- ##
- # Creates an RDoc::Markup::Document given the +groups+ of ChangeLog entries.
+ if last =~ /\s\z/
+ last << continuation
+ else
+ last << ' ' + continuation
+ end
+ end
- def create_document(groups)
- doc = RDoc::Markup::Document.new
- doc.omit_headings_below = 2
- doc.file = @top_level
+ ##
+ # Creates an RDoc::Markup::Document given the +groups+ of ChangeLog entries.
- doc << RDoc::Markup::Heading.new(1, File.basename(@file_name))
- doc << RDoc::Markup::BlankLine.new
+ def create_document(groups)
+ doc = Markup::Document.new
+ doc.omit_headings_below = 2
+ doc.file = @top_level
- groups.sort_by do |day,| day end.reverse_each do |day, entries|
- doc << RDoc::Markup::Heading.new(2, day.dup)
- doc << RDoc::Markup::BlankLine.new
+ doc << Markup::Heading.new(1, File.basename(@file_name))
+ doc << Markup::BlankLine.new
- doc.concat create_entries entries
- end
+ groups.sort_by do |day,| day end.reverse_each do |day, entries|
+ doc << Markup::Heading.new(2, day.dup)
+ doc << Markup::BlankLine.new
- doc
- end
+ doc.concat create_entries entries
+ end
- ##
- # Returns a list of ChangeLog entries an RDoc::Markup nodes for the given
- # +entries+.
+ doc
+ end
- def create_entries(entries)
- out = []
+ ##
+ # Returns a list of ChangeLog entries an RDoc::Markup nodes for the given
+ # +entries+.
- entries.each do |entry, items|
- out << RDoc::Markup::Heading.new(3, entry)
- out << RDoc::Markup::BlankLine.new
+ def create_entries(entries)
+ out = []
- out << create_items(items)
- end
+ entries.each do |entry, items|
+ out << Markup::Heading.new(3, entry)
+ out << Markup::BlankLine.new
- out
- end
+ out << create_items(items)
+ end
- ##
- # Returns an RDoc::Markup::List containing the given +items+ in the
- # ChangeLog
+ out
+ end
- def create_items(items)
- list = RDoc::Markup::List.new :NOTE
+ ##
+ # Returns an RDoc::Markup::List containing the given +items+ in the
+ # ChangeLog
- items.each do |item|
- item =~ /\A(.*?(?:\([^)]+\))?):\s*/
+ def create_items(items)
+ list = Markup::List.new :NOTE
- title = $1
- body = $'
+ items.each do |item|
+ item =~ /\A(.*?(?:\([^)]+\))?):\s*/
- paragraph = RDoc::Markup::Paragraph.new body
- list_item = RDoc::Markup::ListItem.new title, paragraph
- list << list_item
- end
+ title = $1
+ body = $'
- list
- end
+ paragraph = Markup::Paragraph.new body
+ list_item = Markup::ListItem.new title, paragraph
+ list << list_item
+ end
- ##
- # Groups +entries+ by date.
-
- def group_entries(entries)
- @time_cache ||= {}
- entries.group_by do |title, _|
- begin
- time = @time_cache[title]
- (time || parse_date(title)).strftime '%Y-%m-%d'
- rescue NoMethodError, ArgumentError
- time, = title.split ' ', 2
- parse_date(time).strftime '%Y-%m-%d'
+ list
end
- end
- end
- ##
- # Parse date in ISO-8601, RFC-2822, or default of Git
-
- def parse_date(date)
- case date
- when /\A\s*(\d+)-(\d+)-(\d+)(?:[ T](\d+):(\d+):(\d+) *([-+]\d\d):?(\d\d))?\b/
- Time.new($1, $2, $3, $4, $5, $6, ("#{$7}:#{$8}" if $7))
- when /\A\s*\w{3}, +(\d+) (\w{3}) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
- Time.new($3, $2, $1, $4, $5, $6, ("#{$7}:#{$8}" if $7))
- when /\A\s*\w{3} (\w{3}) +(\d+) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
- Time.new($3, $1, $2, $4, $5, $6, ("#{$7}:#{$8}" if $7))
- when /\A\s*\w{3} (\w{3}) +(\d+) (\d+):(\d+):(\d+) (\d+)\b/
- Time.new($6, $1, $2, $3, $4, $5)
- else
- raise ArgumentError, "bad date: #{date}"
- end
- end
-
- ##
- # Parses the entries in the ChangeLog.
- #
- # Returns an Array of each ChangeLog entry in order of parsing.
- #
- # A ChangeLog entry is an Array containing the ChangeLog title (date and
- # committer) and an Array of ChangeLog items (file and function changed with
- # description).
- #
- # An example result would be:
- #
- # [ 'Tue Dec 4 08:33:46 2012 Eric Hodel ',
- # [ 'README.EXT: Converted to RDoc format',
- # 'README.EXT.ja: ditto']]
-
- def parse_entries
- @time_cache ||= {}
-
- if /\A((?:.*\n){,3})commit\s/ =~ @content
- class << self; prepend Git; end
- parse_info($1)
- return parse_entries
- end
-
- entries = []
- entry_name = nil
- entry_body = []
-
- @content.each_line do |line|
- case line
- when /^\s*$/
- next
- when /^\w.*/
- entries << [entry_name, entry_body] if entry_name
+ ##
+ # Groups +entries+ by date.
+
+ def group_entries(entries)
+ @time_cache ||= {}
+ entries.group_by do |title, _|
+ begin
+ time = @time_cache[title]
+ (time || parse_date(title)).strftime '%Y-%m-%d'
+ rescue NoMethodError, ArgumentError
+ time, = title.split ' ', 2
+ parse_date(time).strftime '%Y-%m-%d'
+ end
+ end
+ end
- entry_name = $&
+ ##
+ # Parse date in ISO-8601, RFC-2822, or default of Git
+
+ def parse_date(date)
+ case date
+ when /\A\s*(\d+)-(\d+)-(\d+)(?:[ T](\d+):(\d+):(\d+) *([-+]\d\d):?(\d\d))?\b/
+ Time.new($1, $2, $3, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3}, +(\d+) (\w{3}) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
+ Time.new($3, $2, $1, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
+ Time.new($3, $1, $2, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+):(\d+):(\d+) (\d+)\b/
+ Time.new($6, $1, $2, $3, $4, $5)
+ else
+ raise ArgumentError, "bad date: #{date}"
+ end
+ end
- begin
- time = parse_date entry_name
- @time_cache[entry_name] = time
- rescue ArgumentError
- entry_name = nil
+ ##
+ # Parses the entries in the ChangeLog.
+ #
+ # Returns an Array of each ChangeLog entry in order of parsing.
+ #
+ # A ChangeLog entry is an Array containing the ChangeLog title (date and
+ # committer) and an Array of ChangeLog items (file and function changed with
+ # description).
+ #
+ # An example result would be:
+ #
+ # [ 'Tue Dec 4 08:33:46 2012 Eric Hodel ',
+ # [ 'README.EXT: Converted to RDoc format',
+ # 'README.EXT.ja: ditto']]
+
+ def parse_entries
+ @time_cache ||= {}
+
+ if /\A((?:.*\n){,3})commit\s/ =~ @content
+ class << self; prepend Git; end
+ parse_info($1)
+ return parse_entries
end
+ entries = []
+ entry_name = nil
entry_body = []
- when /^(\t| {8})?\*\s*(.*)/ # "\t* file.c (func): ..."
- entry_body << $2.dup
- when /^(\t| {8})?\s*(\(.*)/ # "\t(func): ..."
- entry = $2
- if entry_body.last =~ /:/
- entry_body << entry.dup
- else
- continue_entry_body entry_body, entry
- end
- when /^(\t| {8})?\s*(.*)/
- continue_entry_body entry_body, $2
- end
- end
+ @content.each_line do |line|
+ case line
+ when /^\s*$/
+ next
+ when /^\w.*/
+ entries << [entry_name, entry_body] if entry_name
- entries << [entry_name, entry_body] if entry_name
+ entry_name = $&
- entries.reject! do |(entry, _)|
- entry == nil
- end
+ begin
+ time = parse_date entry_name
+ @time_cache[entry_name] = time
+ rescue ArgumentError
+ entry_name = nil
+ end
- entries
- end
+ entry_body = []
+ when /^(\t| {8})?\*\s*(.*)/ # "\t* file.c (func): ..."
+ entry_body << $2.dup
+ when /^(\t| {8})?\s*(\(.*)/ # "\t(func): ..."
+ entry = $2
- ##
- # Converts the ChangeLog into an RDoc::Markup::Document
+ if entry_body.last =~ /:/
+ entry_body << entry.dup
+ else
+ continue_entry_body entry_body, entry
+ end
+ when /^(\t| {8})?\s*(.*)/
+ continue_entry_body entry_body, $2
+ end
+ end
- def scan
- @time_cache = {}
+ entries << [entry_name, entry_body] if entry_name
- entries = parse_entries
- grouped_entries = group_entries entries
+ entries.reject! do |(entry, _)|
+ entry == nil
+ end
- doc = create_document grouped_entries
- comment = RDoc::Comment.new(@content)
- comment.document = doc
- @top_level.comment = comment
+ entries
+ end
- @top_level
- end
+ ##
+ # Converts the ChangeLog into an RDoc::Markup::Document
- ##
- # The extension for Git commit log
+ def scan
+ @time_cache = {}
- module Git
- ##
- # Parses auxiliary info. Currently `base-url` to expand
- # references is effective.
+ entries = parse_entries
+ grouped_entries = group_entries entries
- def parse_info(info)
- /^\s*base-url\s*=\s*(.*\S)/ =~ info
- @base_url = $1
- end
+ doc = create_document grouped_entries
+ comment = Comment.new(@content)
+ comment.document = doc
+ @top_level.comment = comment
- ##
- # Parses the entries in the Git commit logs
-
- def parse_entries
- entries = []
-
- @content.scan(/^commit\s+(\h{20})\h*\n((?:.+\n)*)\n((?: {4}.*\n+)*)/) do
- entry_name, header, entry_body = $1, $2, $3.gsub(/^ {4}/, '')
- # header = header.scan(/^ *(\S+?): +(.*)/).to_h
- # date = header["CommitDate"] || header["Date"]
- date = header[/^ *(?:Author)?Date: +(.*)/, 1]
- author = header[/^ *Author: +(.*)/, 1]
- begin
- time = parse_date(header[/^ *CommitDate: +(.*)/, 1] || date)
- @time_cache[entry_name] = time
- author.sub!(/\s*<(.*)>/, '')
- email = $1
- entries << [entry_name, [author, email, date, entry_body]]
- rescue ArgumentError
- end
+ @top_level
end
- entries
- end
+ ##
+ # The extension for Git commit log
- ##
- # Returns a list of ChangeLog entries as
- # RDoc::Parser::ChangeLog::Git::LogEntry list for the given
- # +entries+.
-
- def create_entries(entries)
- # git log entries have no strictly itemized style like the old
- # style, just assume Markdown.
- entries.map do |commit, entry|
- LogEntry.new(@base_url, commit, *entry)
- end
- end
+ module Git
+ ##
+ # Parses auxiliary info. Currently `base-url` to expand
+ # references is effective.
- LogEntry = Struct.new(:base, :commit, :author, :email, :date, :contents) do
- HEADING_LEVEL = 3
-
- def initialize(base, commit, author, email, date, contents)
- case contents
- when String
- if base&.match(%r[\A([^:/]+:/+[^/]+/)[^/]+/[^/]+/])
- repo, host = $&, $1
- contents = contents.dup
- # base: https://github.com/ruby/ruby/
- # Fix #15791 -> Fix [#15791](https://github.com/ruby/ruby/pull/15791)
- # GH-15791 -> [GH-15791](https://github.com/ruby/ruby/pull/15791)
- # (#15791) -> ([#15791](https://github.com/ruby/ruby/pull/15791))
- contents.gsub!(/\b(?:(?i:fix(?:e[sd])?) +)\K\#(\d+\b)|\bGH-(\d+)\b|\(\K\#(\d+)(?=\))/) do
- "[#{$&}](#{repo}pull/#{$1 || $2 || $3})"
- end
- # repo#PR, repo@HASH
- # ruby/ruby#15791 -> [ruby/ruby#15791](https://github.com/ruby/ruby/pull/15791)
- # ruby/ruby@a8a989b6 -> [ruby/ruby@a8a989b6](https://github.com/ruby/ruby/commit/a8a989b6)
- # ref in branckets is not extended
- # [ruby/net-imap#543][ruby/ruby#15791] -> [ruby/net-imap#543][ruby/ruby#15791]
- contents.gsub!(%r[(?/, '')
+ email = $1
+ entries << [entry_name, [author, email, date, entry_body]]
+ rescue ArgumentError
end
end
- case first = contents[0]
- when RDoc::Markup::Paragraph
- contents[0] = RDoc::Markup::Heading.new(HEADING_LEVEL + 1, first.text)
- end
+
+ entries
end
- super
- end
- def level
- HEADING_LEVEL
- end
+ ##
+ # Returns a list of ChangeLog entries as
+ # RDoc::Parser::ChangeLog::Git::LogEntry list for the given
+ # +entries+.
- def aref
- commit
- end
+ def create_entries(entries)
+ # git log entries have no strictly itemized style like the old
+ # style, just assume Markdown.
+ entries.map do |commit, entry|
+ LogEntry.new(@base_url, commit, *entry)
+ end
+ end
- def legacy_aref
- "label-#{commit}"
- end
+ LogEntry = Struct.new(:base, :commit, :author, :email, :date, :contents) do
+ HEADING_LEVEL = 3
+
+ def initialize(base, commit, author, email, date, contents)
+ case contents
+ when String
+ if base&.match(%r[\A([^:/]+:/+[^/]+/)[^/]+/[^/]+/])
+ repo, host = $&, $1
+ contents = contents.dup
+ # base: https://github.com/ruby/ruby/
+ # Fix #15791 -> Fix [#15791](https://github.com/ruby/ruby/pull/15791)
+ # GH-15791 -> [GH-15791](https://github.com/ruby/ruby/pull/15791)
+ # (#15791) -> ([#15791](https://github.com/ruby/ruby/pull/15791))
+ contents.gsub!(/\b(?:(?i:fix(?:e[sd])?) +)\K\#(\d+\b)|\bGH-(\d+)\b|\(\K\#(\d+)(?=\))/) do
+ "[#{$&}](#{repo}pull/#{$1 || $2 || $3})"
+ end
+ # repo#PR, repo@HASH
+ # ruby/ruby#15791 -> [ruby/ruby#15791](https://github.com/ruby/ruby/pull/15791)
+ # ruby/ruby@a8a989b6 -> [ruby/ruby@a8a989b6](https://github.com/ruby/ruby/commit/a8a989b6)
+ # ref in branckets is not extended
+ # [ruby/net-imap#543][ruby/ruby#15791] -> [ruby/net-imap#543][ruby/ruby#15791]
+ contents.gsub!(%r[(?(mod, name, mode) {
- created =
- case mode
- when :class
- mod.add_class(RDoc::NormalClass, name, 'Object').tap { |m| m.store = @store }
- when :module
- mod.add_module(RDoc::NormalModule, name).tap { |m| m.store = @store }
- end
- # add_class/add_module may return an existing object created by another
- # file (in_files is not empty then), which must not be ignored here.
- # Documentable again when reopened or receiving contents outside the region.
- created.ignore if document_suppressed? && created.in_files.empty?
- created
- }
- if root_name.empty?
- mod = @top_level
- else
- @module_nesting.reverse_each do |nesting, singleton|
- next if singleton
- mod = nesting.get_module_named(root_name)
- break if mod
- # If a constant is found and it is not a module or class, RDoc can't document about it.
- # Return an anonymous module to avoid wrong document creation.
- return RDoc::NormalModule.new(nil) if nesting.find_constant_named(root_name)
+ # Adds a method defined by `def` syntax
+
+ def add_method(method_name, receiver_name:, receiver_fallback_type:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:, start_line:, args_end_line:, end_line:)
+ comment, directives, type_signature_lines = consecutive_comment(start_line)
+ apply_document_control_directive(directives) if directives
+ handle_code_object_directives(@container, directives) if directives
+ # Resolve receiver after applying directives so that a namespace created
+ # here is marked as ignored when the comment starts a :stopdoc: region
+ receiver = receiver_name ? find_or_create_lexical_module_path(receiver_name, receiver_fallback_type) : @container
+
+ internal_add_method(
+ method_name,
+ receiver,
+ comment: comment,
+ directives: directives,
+ modifier_comment_lines: [start_line, args_end_line, end_line].uniq,
+ line_no: start_line,
+ visibility: visibility,
+ singleton: singleton,
+ params: params,
+ calls_super: calls_super,
+ block_params: block_params,
+ tokens: tokens,
+ type_signature_lines: type_signature_lines
+ )
end
- last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
- return mod || add_module.call(last_nesting, root_name, create_mode) unless name
- mod ||= add_module.call(last_nesting, root_name, :module)
- end
- path.each do |name|
- mod = mod.get_module_named(name) || add_module.call(mod, name, :module)
- end
- mod.get_module_named(name) || add_module.call(mod, name, create_mode)
- end
- # Resolves constant path to a full path by searching module nesting
+ private def internal_add_method(method_name, container, comment:, dont_rename_initialize: false, directives:, modifier_comment_lines: nil, line_no:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:, type_signature_lines: nil) # :nodoc:
+ meth = AnyMethod.new(method_name, singleton: singleton)
+ meth.comment = comment
+ handle_code_object_directives(meth, directives) if directives
+ modifier_comment_lines&.each do |line|
+ handle_modifier_directive(meth, line)
+ end
+ return if document_suppressed?
+ return unless should_document?(meth)
- def resolve_constant_path(constant_path)
- owner_name, path = constant_path.split('::', 2)
- return constant_path if owner_name.empty? # ::Foo, ::Foo::Bar
- mod = nil
- @module_nesting.reverse_each do |nesting, singleton|
- next if singleton
- mod = nesting.get_module_named(owner_name)
- break if mod
- end
- mod ||= @top_level.get_module_named(owner_name)
- [mod.full_name, path].compact.join('::') if mod
- end
+ mark_container_documentable(container)
- # Returns a pair of owner module and constant name from a given constant path
- # using Ruby lexical nesting. Creates owner module if it does not exist.
-
- def find_or_create_lexical_constant_owner_name(constant_path)
- const_path, colon, name = constant_path.rpartition('::')
- if colon.empty? # class Foo
- # Within `class C` or `module C`, owner is C(== current container)
- # Within `class <(mod, name, mode) {
+ created =
+ case mode
+ when :class
+ mod.add_class(NormalClass, name, 'Object').tap { |m| m.store = @store }
+ when :module
+ mod.add_module(NormalModule, name).tap { |m| m.store = @store }
+ end
+ # add_class/add_module may return an existing object created by another
+ # file (in_files is not empty then), which must not be ignored here.
+ # Documentable again when reopened or receiving contents outside the region.
+ created.ignore if document_suppressed? && created.in_files.empty?
+ created
+ }
+ if root_name.empty?
+ mod = @top_level
+ else
+ @module_nesting.reverse_each do |nesting, singleton|
+ next if singleton
+ mod = nesting.get_module_named(root_name)
+ break if mod
+ # If a constant is found and it is not a module or class, RDoc can't document about it.
+ # Return an anonymous module to avoid wrong document creation.
+ return NormalModule.new(nil) if nesting.find_constant_named(root_name)
+ end
+ last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
+ return mod || add_module.call(last_nesting, root_name, create_mode) unless name
+ mod ||= add_module.call(last_nesting, root_name, :module)
+ end
+ path.each do |name|
+ mod = mod.get_module_named(name) || add_module.call(mod, name, :module)
+ end
+ mod.get_module_named(name) || add_module.call(mod, name, create_mode)
end
- # Superclass with the same full path and superclass for BasicObject are not allowed
- if superclass_name && mod.full_name != superclass_full_path && mod.full_name != 'BasicObject'
- if superclass
- mod.superclass = superclass
- elsif mod.superclass.nil? || (mod.superclass.is_a?(String) || mod.superclass.name == 'Object') && mod.superclass != superclass_full_path
- mod.superclass = superclass_full_path
+ # Resolves constant path to a full path by searching module nesting
+
+ def resolve_constant_path(constant_path)
+ owner_name, path = constant_path.split('::', 2)
+ return constant_path if owner_name.empty? # ::Foo, ::Foo::Bar
+ mod = nil
+ @module_nesting.reverse_each do |nesting, singleton|
+ next if singleton
+ mod = nesting.get_module_named(owner_name)
+ break if mod
end
+ mod ||= @top_level.get_module_named(owner_name)
+ [mod.full_name, path].compact.join('::') if mod
end
- else
- mod = owner.modules_hash[name]
- unless mod
- mod = owner.add_module(RDoc::NormalModule, name)
- mod.ignore if document_suppressed? && mod.in_files.empty?
+
+ # Returns a pair of owner module and constant name from a given constant path
+ # using Ruby lexical nesting. Creates owner module if it does not exist.
+
+ def find_or_create_lexical_constant_owner_name(constant_path)
+ const_path, colon, name = constant_path.rpartition('::')
+ if colon.empty? # class Foo
+ # Within `class C` or `module C`, owner is C(== current container)
+ # Within `class < 0
- colored_tokens
- end
+module RDoc
+ class Parser
+ # Ruby code syntax highlighter.
+ # Colorize result is an array of +RDoc::Parser::RubyColorizer::ColoredToken+
+ # Actual color for each token kind is determined elsewhere (e.g., HTML generator)
+ module RubyColorizer
+
+ ColoredToken = Struct.new(:kind, :text)
+
+ # Prism operator token types except assignment '='
+ OP_TOKENS = %i[
+ AMPERSAND AMPERSAND_AMPERSAND
+ BANG BANG_EQUAL BANG_TILDE CARET COLON COLON_COLON
+ EQUAL_EQUAL EQUAL_GREATER EQUAL_TILDE
+ GREATER GREATER_GREATER
+ LESS LESS_EQUAL LESS_EQUAL_GREATER LESS_LESS
+ MINUS MINUS_GREATER PERCENT PIPE PIPE_PIPE PLUS
+ QUESTION_MARK SLASH STAR STAR_STAR TILDE
+ UAMPERSAND UMINUS UPLUS USTAR USTAR_STAR
+ ].to_set
+
+ # Prism token type to ColoredToken kind map
+ TOKEN_TYPE_MAP = {
+ IDENTIFIER: :identifier,
+ METHOD_NAME: :identifier,
+ INSTANCE_VARIABLE: :ivar,
+ CLASS_VARIABLE: :identifier,
+ GLOBAL_VARIABLE: :identifier,
+ BACK_REFERENCE: :identifier,
+ NUMBERED_REFERENCE: :identifier,
+ CONSTANT: :constant,
+ LABEL: :value,
+ INTEGER: :value,
+ INTEGER_IMAGINARY: :value,
+ INTEGER_RATIONAL: :value,
+ INTEGER_RATIONAL_IMAGINARY: :value,
+ FLOAT: :value,
+ FLOAT_IMAGINARY: :value,
+ FLOAT_RATIONAL: :value,
+ FLOAT_RATIONAL_IMAGINARY: :value,
+ COMMENT: :comment,
+ EMBDOC_BEGIN: :comment,
+ EMBDOC_LINE: :comment,
+ EMBDOC_END: :comment
+ }
- private
+ class << self
- def slice_by_location(items, start_offset, end_offset)
- start_index = items.bsearch_index { |item| item.location.end_offset > start_offset } || items.size
- end_index = items.bsearch_index { |item| item.location.start_offset >= end_offset } || items.size
- items[start_index...end_index]
- end
+ # Colorize the entire +code+ and returns colored token stream.
+ def colorize(code)
+ result = Prism.parse_lex(code)
+ program_node, unordered_tokens = result.value
+ prism_tokens = unordered_tokens.map(&:first).sort_by! { |token| token.location.start_offset }
+ partial_colorize(code, program_node, prism_tokens, 0, code.bytesize)
+ end
- # Unify prior tokens and normal tokens into a single token stream.
- # Prior tokens have higher priority than normal tokens.
- # Also adds missing text (spaces, newlines, etc.) as :plain tokens
- # so that the entire range is covered.
- def unify_tokens(whole_code, prior_tokens, normal_tokens, start_offset, end_offset)
- tokens = []
- offset = start_offset
+ # Colorize partial +node+ in +whole_code+ and returns colored token stream.
+ def partial_colorize(whole_code, node, prism_tokens, start_offset = nil, end_offset = nil)
+ start_offset ||= node.location.start_offset
+ end_offset ||= node.location.end_offset
+ visitor = NodeColorizeVisitor.new
+ node.accept(visitor)
+ prior_tokens = visitor.tokens.sort_by {|_, start_offset, _| start_offset }
+ normal_tokens = normal_tokens(slice_by_location(prism_tokens, start_offset, end_offset))
+ colored_tokens = unify_tokens(whole_code, prior_tokens, normal_tokens, start_offset, end_offset)
+ colored_tokens.unshift(ColoredToken.new(:plain, ' ' * node.location.start_column)) if node.location.start_column > 0
+ colored_tokens
+ end
- # Add missing text such as spaces and newlines as a separate :plain token
- flush = -> next_offset {
- return if offset == next_offset
+ private
- whole_code.byteslice(offset...next_offset).scan(/\n|\s+|[^\s]+/) do |text|
- tokens << ColoredToken.new(:plain, text)
+ def slice_by_location(items, start_offset, end_offset)
+ start_index = items.bsearch_index { |item| item.location.end_offset > start_offset } || items.size
+ end_index = items.bsearch_index { |item| item.location.start_offset >= end_offset } || items.size
+ items[start_index...end_index]
end
- }
- until prior_tokens.empty? && normal_tokens.empty?
- ptok = prior_tokens.first
- ntok = normal_tokens.first
- if ntok && (!ptok || ntok[2] <= ptok[1])
- token = normal_tokens.shift
- else
- token = prior_tokens.shift
+ # Unify prior tokens and normal tokens into a single token stream.
+ # Prior tokens have higher priority than normal tokens.
+ # Also adds missing text (spaces, newlines, etc.) as :plain tokens
+ # so that the entire range is covered.
+ def unify_tokens(whole_code, prior_tokens, normal_tokens, start_offset, end_offset)
+ tokens = []
+ offset = start_offset
+
+ # Add missing text such as spaces and newlines as a separate :plain token
+ flush = -> next_offset {
+ return if offset == next_offset
+
+ whole_code.byteslice(offset...next_offset).scan(/\n|\s+|[^\s]+/) do |text|
+ tokens << ColoredToken.new(:plain, text)
+ end
+ }
+
+ until prior_tokens.empty? && normal_tokens.empty?
+ ptok = prior_tokens.first
+ ntok = normal_tokens.first
+ if ntok && (!ptok || ntok[2] <= ptok[1])
+ token = normal_tokens.shift
+ else
+ token = prior_tokens.shift
+ end
+ kind, start_pos, end_pos = token
+ next if start_pos < offset
+
+ flush.call(start_pos)
+ tokens << ColoredToken.new(kind, whole_code.byteslice(start_pos...end_pos))
+ offset = end_pos
+ end
+ flush.call(end_offset)
+ tokens
end
- kind, start_pos, end_pos = token
- next if start_pos < offset
-
- flush.call(start_pos)
- tokens << ColoredToken.new(kind, whole_code.byteslice(start_pos...end_pos))
- offset = end_pos
- end
- flush.call(end_offset)
- tokens
- end
- # Convert normal Prism tokens to [kind, start_offset, end_offset]
- def normal_tokens(tokens)
- tokens.map do |token,|
- kind =
- if token.type.start_with?('KEYWORD_')
- :keyword
- elsif OP_TOKENS.include?(token.type.to_sym)
- :operator
- else
- TOKEN_TYPE_MAP[token.type] || :plain
+ # Convert normal Prism tokens to [kind, start_offset, end_offset]
+ def normal_tokens(tokens)
+ tokens.map do |token,|
+ kind =
+ if token.type.start_with?('KEYWORD_')
+ :keyword
+ elsif OP_TOKENS.include?(token.type.to_sym)
+ :operator
+ else
+ TOKEN_TYPE_MAP[token.type] || :plain
+ end
+ [kind, token.location.start_offset, token.location.end_offset]
end
- [kind, token.location.start_offset, token.location.end_offset]
+ end
end
- end
- end
- # Visitor to determine node colorizing which can't be determined by tokens.
- # STRING_CONTENT/EMBEXPR_BEGIN/EMBEXPR_END in string/regexp/symbol have different colorizing
- class NodeColorizeVisitor < Prism::Visitor # :nodoc:
- attr_reader :tokens
+ # Visitor to determine node colorizing which can't be determined by tokens.
+ # STRING_CONTENT/EMBEXPR_BEGIN/EMBEXPR_END in string/regexp/symbol have different colorizing
+ class NodeColorizeVisitor < Prism::Visitor # :nodoc:
+ attr_reader :tokens
- def initialize
- @tokens = []
- end
+ def initialize
+ @tokens = []
+ end
- def visit_symbol_node(node)
- # SymbolNode#location may contain heredoc content and closing
- # e.g., `<; end`
- push_location(:identifier, node.name_loc)
- super
- end
+ def visit_def_node(node)
+ # For special colorizing of method name in def node
+ # e.g., `def <=>; end`
+ push_location(:identifier, node.name_loc)
+ super
+ end
- private
+ private
- def push_location(kind, location)
- # Only push tokens that have a non-zero length
- if location && location.start_offset < location.end_offset
- @tokens << [kind, location.start_offset, location.end_offset]
- end
- end
+ def push_location(kind, location)
+ # Only push tokens that have a non-zero length
+ if location && location.start_offset < location.end_offset
+ @tokens << [kind, location.start_offset, location.end_offset]
+ end
+ end
- def handle_interpolated_parts(kind, parts)
- # StringNode, EmbeddedStatementsNode brackets, and EmbeddedVariableNode hash in
- # interpolated regexp/symbol/string parts should be colored as regexp/symbol/string respectively.
- parts.each do |part|
- case part
- when Prism::StringNode
- # InterpolatedStringNode#parts may have its own opening/closing. e.g., `'a' "b"`
- push_location(kind, part.opening_loc)
- push_location(kind, part.content_loc)
- push_location(kind, part.closing_loc)
- when Prism::InterpolatedStringNode
- # InterpolatedStringNode#parts may contain InterpolatedStringNode. e.g., `'a' "#{}"`
- part.accept(self)
- when Prism::EmbeddedStatementsNode
- push_location(kind, part.opening_loc)
- push_location(kind, part.closing_loc)
- part.accept(self)
- when Prism::EmbeddedVariableNode
- push_location(kind, part.operator_loc)
+ def handle_interpolated_parts(kind, parts)
+ # StringNode, EmbeddedStatementsNode brackets, and EmbeddedVariableNode hash in
+ # interpolated regexp/symbol/string parts should be colored as regexp/symbol/string respectively.
+ parts.each do |part|
+ case part
+ when Prism::StringNode
+ # InterpolatedStringNode#parts may have its own opening/closing. e.g., `'a' "b"`
+ push_location(kind, part.opening_loc)
+ push_location(kind, part.content_loc)
+ push_location(kind, part.closing_loc)
+ when Prism::InterpolatedStringNode
+ # InterpolatedStringNode#parts may contain InterpolatedStringNode. e.g., `'a' "#{}"`
+ part.accept(self)
+ when Prism::EmbeddedStatementsNode
+ push_location(kind, part.opening_loc)
+ push_location(kind, part.closing_loc)
+ part.accept(self)
+ when Prism::EmbeddedVariableNode
+ push_location(kind, part.operator_loc)
+ end
+ end
end
end
+
+ private_constant :NodeColorizeVisitor
end
end
-
- private_constant :NodeColorizeVisitor
end
diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb
index a0edca1b33..8274a05e82 100644
--- a/lib/rdoc/parser/simple.rb
+++ b/lib/rdoc/parser/simple.rb
@@ -1,44 +1,48 @@
# frozen_string_literal: true
-##
-# Parse a non-source file. We basically take the whole thing as one big
-# comment.
+module RDoc
+ class Parser
+ ##
+ # Parse a non-source file. We basically take the whole thing as one big
+ # comment.
-class RDoc::Parser::Simple < RDoc::Parser
+ class Simple < Parser
- include RDoc::Parser::Text
+ include Parser::Text
- parse_files_matching(//)
+ parse_files_matching(//)
- attr_reader :content # :nodoc:
+ attr_reader :content # :nodoc:
- ##
- # Prepare to parse a plain file
+ ##
+ # Prepare to parse a plain file
- def initialize(top_level, content, options, stats)
- super
+ def initialize(top_level, content, options, stats)
+ super
- preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+ preprocess = Markup::PreProcess.new @file_name, @options.rdoc_include
- content = RDoc::Text.expand_tabs(@content)
- @content, = preprocess.run_pre_processes(content, @top_level, 1, :simple)
- end
+ content = ::RDoc::Text.expand_tabs(@content)
+ @content, = preprocess.run_pre_processes(content, @top_level, 1, :simple)
+ end
- ##
- # Extract the file contents and attach them to the TopLevel as a comment
+ ##
+ # Extract the file contents and attach them to the TopLevel as a comment
- def scan
- content = remove_coding_comment @content
+ def scan
+ content = remove_coding_comment @content
- comment = RDoc::Comment.new content, @top_level
+ comment = Comment.new content, @top_level
- @top_level.comment = comment
- @top_level
- end
+ @top_level.comment = comment
+ @top_level
+ end
- ##
- # Removes the encoding magic comment from +text+
+ ##
+ # Removes the encoding magic comment from +text+
- def remove_coding_comment(text)
- text.sub(/\A# .*coding[=:].*$/, '')
+ def remove_coding_comment(text)
+ text.sub(/\A# .*coding[=:].*$/, '')
+ end
+ end
end
end
diff --git a/lib/rdoc/parser/text.rb b/lib/rdoc/parser/text.rb
index 5095d8cc64..46fb8efec3 100644
--- a/lib/rdoc/parser/text.rb
+++ b/lib/rdoc/parser/text.rb
@@ -1,11 +1,15 @@
# frozen_string_literal: true
-##
-# Indicates this parser is text and doesn't contain code constructs.
-#
-# Include this module in a RDoc::Parser subclass to make it show up as a file,
-# not as part of a class or module.
-#--
-# This is not named File to avoid overriding ::File
+module RDoc
+ class Parser
+ ##
+ # Indicates this parser is text and doesn't contain code constructs.
+ #
+ # Include this module in a RDoc::Parser subclass to make it show up as a file,
+ # not as part of a class or module.
+ #--
+ # This is not named File to avoid overriding ::File
-module RDoc::Parser::Text
+ module Text
+ end
+ end
end
diff --git a/lib/rdoc/rbs_helper.rb b/lib/rdoc/rbs_helper.rb
index 5750663eee..513474dcac 100644
--- a/lib/rdoc/rbs_helper.rb
+++ b/lib/rdoc/rbs_helper.rb
@@ -122,7 +122,7 @@ def link_type_names_in_line(line, lookup, from_path)
start_in_escaped = prefix.length
end_in_escaped = start_in_escaped + escaped_name.length
- href = ERB::Util.html_escape(::RDoc::Markup::Formatter.gen_relative_url(from_path, target_path))
+ href = ERB::Util.html_escape(Markup::Formatter.gen_relative_url(from_path, target_path))
result[start_in_escaped...end_in_escaped] =
"#{escaped_name}"
end
diff --git a/lib/rdoc/rd.rb b/lib/rdoc/rd.rb
index e845543ac4..e0139e16ad 100644
--- a/lib/rdoc/rd.rb
+++ b/lib/rdoc/rd.rb
@@ -1,99 +1,101 @@
# frozen_string_literal: true
-##
-# RDoc::RD implements the RD format from the rdtool gem.
-#
-# To choose RD as your only default format see
-# RDoc::Options@Saved+Options for instructions on setting up a
-# .doc_options file to store your project default.
-#
-# == LICENSE
-#
-# The grammar that produces RDoc::RD::BlockParser and RDoc::RD::InlineParser
-# is included in RDoc under the Ruby License.
-#
-# You can find the original source for rdtool at
-# https://github.com/uwabami/rdtool/
-#
-# You can use, re-distribute or change these files under Ruby's License or GPL.
-#
-# 1. You may make and give away verbatim copies of the source form of the
-# software without restriction, provided that you duplicate all of the
-# original copyright notices and associated disclaimers.
-#
-# 2. You may modify your copy of the software in any way, provided that
-# you do at least ONE of the following:
-#
-# a. place your modifications in the Public Domain or otherwise
-# make them Freely Available, such as by posting said
-# modifications to Usenet or an equivalent medium, or by allowing
-# the author to include your modifications in the software.
-#
-# b. use the modified software only within your corporation or
-# organization.
-#
-# c. give non-standard binaries non-standard names, with
-# instructions on where to get the original software distribution.
-#
-# d. make other distribution arrangements with the author.
-#
-# 3. You may distribute the software in object code or binary form,
-# provided that you do at least ONE of the following:
-#
-# a. distribute the binaries and library files of the software,
-# together with instructions (in the manual page or equivalent)
-# on where to get the original distribution.
-#
-# b. accompany the distribution with the machine-readable source of
-# the software.
-#
-# c. give non-standard binaries non-standard names, with
-# instructions on where to get the original software distribution.
-#
-# d. make other distribution arrangements with the author.
-#
-# 4. You may modify and include the part of the software into any other
-# software (possibly commercial). But some files in the distribution
-# are not written by the author, so that they are not under these terms.
-#
-# For the list of those files and their copying conditions, see the
-# file LEGAL.
-#
-# 5. The scripts and library files supplied as input to or produced as
-# output from the software do not automatically fall under the
-# copyright of the software, but belong to whomever generated them,
-# and may be sold commercially, and may be aggregated with this
-# software.
-#
-# 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE.
+module RDoc
+ ##
+ # RDoc::RD implements the RD format from the rdtool gem.
+ #
+ # To choose RD as your only default format see
+ # RDoc::Options@Saved+Options for instructions on setting up a
+ # .doc_options file to store your project default.
+ #
+ # == LICENSE
+ #
+ # The grammar that produces RDoc::RD::BlockParser and RDoc::RD::InlineParser
+ # is included in RDoc under the Ruby License.
+ #
+ # You can find the original source for rdtool at
+ # https://github.com/uwabami/rdtool/
+ #
+ # You can use, re-distribute or change these files under Ruby's License or GPL.
+ #
+ # 1. You may make and give away verbatim copies of the source form of the
+ # software without restriction, provided that you duplicate all of the
+ # original copyright notices and associated disclaimers.
+ #
+ # 2. You may modify your copy of the software in any way, provided that
+ # you do at least ONE of the following:
+ #
+ # a. place your modifications in the Public Domain or otherwise
+ # make them Freely Available, such as by posting said
+ # modifications to Usenet or an equivalent medium, or by allowing
+ # the author to include your modifications in the software.
+ #
+ # b. use the modified software only within your corporation or
+ # organization.
+ #
+ # c. give non-standard binaries non-standard names, with
+ # instructions on where to get the original software distribution.
+ #
+ # d. make other distribution arrangements with the author.
+ #
+ # 3. You may distribute the software in object code or binary form,
+ # provided that you do at least ONE of the following:
+ #
+ # a. distribute the binaries and library files of the software,
+ # together with instructions (in the manual page or equivalent)
+ # on where to get the original distribution.
+ #
+ # b. accompany the distribution with the machine-readable source of
+ # the software.
+ #
+ # c. give non-standard binaries non-standard names, with
+ # instructions on where to get the original software distribution.
+ #
+ # d. make other distribution arrangements with the author.
+ #
+ # 4. You may modify and include the part of the software into any other
+ # software (possibly commercial). But some files in the distribution
+ # are not written by the author, so that they are not under these terms.
+ #
+ # For the list of those files and their copying conditions, see the
+ # file LEGAL.
+ #
+ # 5. The scripts and library files supplied as input to or produced as
+ # output from the software do not automatically fall under the
+ # copyright of the software, but belong to whomever generated them,
+ # and may be sold commercially, and may be aggregated with this
+ # software.
+ #
+ # 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ # PURPOSE.
-class RDoc::RD
+ class RD
- ##
- # Parses +rd+ source and returns an RDoc::Markup::Document. If the
- # =begin or =end lines are missing they will be added.
+ ##
+ # Parses +rd+ source and returns an RDoc::Markup::Document. If the
+ # =begin or =end lines are missing they will be added.
- def self.parse(rd)
- rd = rd.lines.to_a
+ def self.parse(rd)
+ rd = rd.lines.to_a
- if rd.find { |i| /\S/ === i } and !rd.find{|i| /^=begin\b/ === i }
- rd.unshift("=begin\n").push("=end\n")
- end
+ if rd.find { |i| /\S/ === i } and !rd.find{|i| /^=begin\b/ === i }
+ rd.unshift("=begin\n").push("=end\n")
+ end
- parser = RDoc::RD::BlockParser.new
- document = parser.parse rd
+ parser = RD::BlockParser.new
+ document = parser.parse rd
- # isn't this always true?
- document.parts.shift if RDoc::Markup::BlankLine === document.parts.first
- document.parts.pop if RDoc::Markup::BlankLine === document.parts.last
+ # isn't this always true?
+ document.parts.shift if Markup::BlankLine === document.parts.first
+ document.parts.pop if Markup::BlankLine === document.parts.last
- document
- end
+ document
+ end
- autoload :BlockParser, "#{__dir__}/rd/block_parser"
- autoload :InlineParser, "#{__dir__}/rd/inline_parser"
- autoload :Inline, "#{__dir__}/rd/inline"
+ autoload :BlockParser, "#{__dir__}/rd/block_parser"
+ autoload :InlineParser, "#{__dir__}/rd/inline_parser"
+ autoload :Inline, "#{__dir__}/rd/inline"
+ end
end
diff --git a/lib/rdoc/rd/inline.rb b/lib/rdoc/rd/inline.rb
index 1c76e81f76..c3e055d83b 100644
--- a/lib/rdoc/rd/inline.rb
+++ b/lib/rdoc/rd/inline.rb
@@ -1,71 +1,75 @@
# frozen_string_literal: true
-##
-# Inline keeps track of markup and labels to create proper links.
+module RDoc
+ class RD
+ ##
+ # Inline keeps track of markup and labels to create proper links.
-class RDoc::RD::Inline
+ class Inline
- ##
- # The text of the reference
+ ##
+ # The text of the reference
- attr_reader :reference
+ attr_reader :reference
- ##
- # The markup of this reference in RDoc format
+ ##
+ # The markup of this reference in RDoc format
- attr_reader :rdoc
+ attr_reader :rdoc
- ##
- # Creates a new Inline for +rdoc+ and +reference+.
- #
- # +rdoc+ may be another Inline or a String. If +reference+ is not given it
- # will use the text from +rdoc+.
+ ##
+ # Creates a new Inline for +rdoc+ and +reference+.
+ #
+ # +rdoc+ may be another Inline or a String. If +reference+ is not given it
+ # will use the text from +rdoc+.
- def self.new(rdoc, reference = rdoc)
- if self === rdoc and reference.equal? rdoc
- rdoc
- else
- super
- end
- end
+ def self.new(rdoc, reference = rdoc)
+ if self === rdoc and reference.equal? rdoc
+ rdoc
+ else
+ super
+ end
+ end
- ##
- # Initializes the Inline with +rdoc+ and +inline+
+ ##
+ # Initializes the Inline with +rdoc+ and +inline+
- def initialize(rdoc, reference) # :not-new:
- @reference = reference.equal?(rdoc) ? reference.dup : reference
+ def initialize(rdoc, reference) # :not-new:
+ @reference = reference.equal?(rdoc) ? reference.dup : reference
- # unpack
- @reference = @reference.reference if self.class === @reference
- @rdoc = rdoc
- end
+ # unpack
+ @reference = @reference.reference if self.class === @reference
+ @rdoc = rdoc
+ end
- def ==(other) # :nodoc:
- self.class === other and
- @reference == other.reference and @rdoc == other.rdoc
- end
+ def ==(other) # :nodoc:
+ self.class === other and
+ @reference == other.reference and @rdoc == other.rdoc
+ end
- ##
- # Appends +more+ to this inline. +more+ may be a String or another Inline.
-
- def append(more)
- case more
- when String
- @reference += more
- @rdoc += more
- when RDoc::RD::Inline
- @reference += more.reference
- @rdoc += more.rdoc
- else
- raise "unknown thingy #{more}"
- end
+ ##
+ # Appends +more+ to this inline. +more+ may be a String or another Inline.
- self
- end
+ def append(more)
+ case more
+ when String
+ @reference += more
+ @rdoc += more
+ when RD::Inline
+ @reference += more.reference
+ @rdoc += more.rdoc
+ else
+ raise "unknown thingy #{more}"
+ end
- def inspect # :nodoc:
- "(inline: #{self})"
- end
+ self
+ end
+
+ def inspect # :nodoc:
+ "(inline: #{self})"
+ end
- alias to_s rdoc # :nodoc:
+ alias to_s rdoc # :nodoc:
+ end
+ end
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index fefa83c6ba..0aa91ba111 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -7,191 +7,192 @@
require 'time'
require_relative 'rbs_helper'
-##
-# This is the driver for generating RDoc output. It handles file parsing and
-# generation of output.
-#
-# To use this class to generate RDoc output via the API, the recommended way
-# is:
-#
-# rdoc = RDoc::RDoc.new
-# options = RDoc::Options.load_options # returns an RDoc::Options instance
-# # set extra options
-# rdoc.document options
-#
-# You can also generate output like the +rdoc+ executable:
-#
-# rdoc = RDoc::RDoc.new
-# rdoc.document argv
-#
-# Where +argv+ is an array of strings, each corresponding to an argument you'd
-# give rdoc on the command line. See rdoc --help for details.
-
-class RDoc::RDoc
-
- @current = nil
-
+module RDoc
##
- # This is the list of supported output generators
+ # This is the driver for generating RDoc output. It handles file parsing and
+ # generation of output.
+ #
+ # To use this class to generate RDoc output via the API, the recommended way
+ # is:
+ #
+ # rdoc = RDoc::RDoc.new
+ # options = RDoc::Options.load_options # returns an RDoc::Options instance
+ # # set extra options
+ # rdoc.document options
+ #
+ # You can also generate output like the +rdoc+ executable:
+ #
+ # rdoc = RDoc::RDoc.new
+ # rdoc.document argv
+ #
+ # Where +argv+ is an array of strings, each corresponding to an argument you'd
+ # give rdoc on the command line. See rdoc --help for details.
- GENERATORS = {}
+ class RDoc
- ##
- # List of directory names always skipped
+ @current = nil
- UNCONDITIONALLY_SKIPPED_DIRECTORIES = %w[CVS .svn .git].freeze
+ ##
+ # This is the list of supported output generators
- ##
- # List of directory names skipped if test suites should be skipped
+ GENERATORS = {}
- TEST_SUITE_DIRECTORY_NAMES = %w[spec test].freeze
+ ##
+ # List of directory names always skipped
+ UNCONDITIONALLY_SKIPPED_DIRECTORIES = %w[CVS .svn .git].freeze
- ##
- # Generator instance used for creating output
+ ##
+ # List of directory names skipped if test suites should be skipped
- attr_accessor :generator
+ TEST_SUITE_DIRECTORY_NAMES = %w[spec test].freeze
- ##
- # Hash of files and their last modified times.
- attr_reader :last_modified
+ ##
+ # Generator instance used for creating output
- ##
- # RDoc options
+ attr_accessor :generator
- attr_accessor :options
+ ##
+ # Hash of files and their last modified times.
- ##
- # Accessor for statistics. Available after each call to parse_files
+ attr_reader :last_modified
- attr_reader :stats
+ ##
+ # RDoc options
- ##
- # The current documentation store
+ attr_accessor :options
- attr_accessor :store
+ ##
+ # Accessor for statistics. Available after each call to parse_files
- ##
- # Add +klass+ that can generate output after parsing
+ attr_reader :stats
- def self.add_generator(klass)
- name = klass.name.sub(/^RDoc::Generator::/, '').downcase
- GENERATORS[name] = klass
- end
+ ##
+ # The current documentation store
- ##
- # Active RDoc::RDoc instance
+ attr_accessor :store
- def self.current
- @current
- end
+ ##
+ # Add +klass+ that can generate output after parsing
- ##
- # Sets the active RDoc::RDoc instance
+ def self.add_generator(klass)
+ name = klass.name.sub(/^RDoc::Generator::/, '').downcase
+ GENERATORS[name] = klass
+ end
- def self.current=(rdoc)
- @current = rdoc
- end
+ ##
+ # Active RDoc::RDoc instance
- ##
- # Creates a new RDoc::RDoc instance. Call #document to parse files and
- # generate documentation.
-
- def initialize
- @current = nil
- @generator = nil
- @last_modified = {}
- @old_siginfo = nil
- @options = nil
- @stats = nil
- @store = nil
- end
+ def self.current
+ @current
+ end
- ##
- # Report an error message and exit
+ ##
+ # Sets the active RDoc::RDoc instance
- def error(msg)
- raise RDoc::Error, msg
- end
+ def self.current=(rdoc)
+ @current = rdoc
+ end
- ##
- # Gathers a set of parseable files from the files and directories listed in
- # +files+.
+ ##
+ # Creates a new RDoc::RDoc instance. Call #document to parse files and
+ # generate documentation.
+
+ def initialize
+ @current = nil
+ @generator = nil
+ @last_modified = {}
+ @old_siginfo = nil
+ @options = nil
+ @stats = nil
+ @store = nil
+ end
- def gather_files(files)
- files = [@options.root.to_s] if files.empty?
+ ##
+ # Report an error message and exit
- file_list = normalized_file_list files, true, @options.exclude
+ def error(msg)
+ raise Error, msg
+ end
+
+ ##
+ # Gathers a set of parseable files from the files and directories listed in
+ # +files+.
+
+ def gather_files(files)
+ files = [@options.root.to_s] if files.empty?
- file_list = remove_duplicate_files(remove_unparseable(file_list))
+ file_list = normalized_file_list files, true, @options.exclude
- if file_list.count {|name, mtime|
- file_list[name] = @last_modified[name] unless mtime
- mtime
- } > 0
- @last_modified.replace file_list
- file_list.keys.sort
- else
- []
+ file_list = remove_duplicate_files(remove_unparseable(file_list))
+
+ if file_list.count {|name, mtime|
+ file_list[name] = @last_modified[name] unless mtime
+ mtime
+ } > 0
+ @last_modified.replace file_list
+ file_list.keys.sort
+ else
+ []
+ end
end
- end
- ##
- # Turns RDoc from stdin into HTML
+ ##
+ # Turns RDoc from stdin into HTML
- def handle_pipe
- @html = RDoc::Markup::ToHtml.new(pipe: @options.pipe, output_decoration: @options.output_decoration)
+ def handle_pipe
+ @html = Markup::ToHtml.new(pipe: @options.pipe, output_decoration: @options.output_decoration)
- parser = RDoc::Text::MARKUP_FORMAT[@options.markup]
+ parser = Text::MARKUP_FORMAT[@options.markup]
- document = parser.parse $stdin.read
+ document = parser.parse $stdin.read
- out = @html.convert document
+ out = @html.convert document
- $stdout.write out
- end
+ $stdout.write out
+ end
- ##
- # Installs a siginfo handler that prints the current filename.
+ ##
+ # Installs a siginfo handler that prints the current filename.
- def install_siginfo_handler
- return unless Signal.list.include? 'INFO'
+ def install_siginfo_handler
+ return unless Signal.list.include? 'INFO'
- @old_siginfo = trap 'INFO' do
- puts @current if @current
+ @old_siginfo = trap 'INFO' do
+ puts @current if @current
+ end
end
- end
- ##
- # Create an output dir if it doesn't exist. If it does exist, but doesn't
- # contain the flag file created.rid then we refuse to use it, as
- # we may clobber some manually generated documentation
+ ##
+ # Create an output dir if it doesn't exist. If it does exist, but doesn't
+ # contain the flag file created.rid then we refuse to use it, as
+ # we may clobber some manually generated documentation
- def setup_output_dir(dir, force)
- flag_file = output_flag_file dir
+ def setup_output_dir(dir, force)
+ flag_file = output_flag_file dir
- last = {}
+ last = {}
- if @options.dry_run
- # do nothing
- elsif File.exist? dir
- error "#{dir} exists and is not a directory" unless File.directory? dir
+ if @options.dry_run
+ # do nothing
+ elsif File.exist? dir
+ error "#{dir} exists and is not a directory" unless File.directory? dir
- begin
- File.open flag_file do |io|
- unless force
- Time.parse io.gets
-
- io.each do |line|
- file, time = line.split "\t", 2
- time = Time.parse(time) rescue next
- last[file] = time
+ begin
+ File.open flag_file do |io|
+ unless force
+ Time.parse io.gets
+
+ io.each do |line|
+ file, time = line.split "\t", 2
+ time = Time.parse(time) rescue next
+ last[file] = time
+ end
end
end
- end
- rescue SystemCallError, TypeError
- error <<-ERROR
+ rescue SystemCallError, TypeError
+ error <<-ERROR
Directory #{dir} already exists, but it looks like it isn't an RDoc directory.
@@ -200,179 +201,179 @@ def setup_output_dir(dir, force)
option)
ERROR
- end unless @options.force_output
- else
- FileUtils.mkdir_p dir
- FileUtils.touch flag_file
+ end unless @options.force_output
+ else
+ FileUtils.mkdir_p dir
+ FileUtils.touch flag_file
+ end
+
+ last
end
- last
- end
+ ##
+ # Update the flag file in an output directory.
- ##
- # Update the flag file in an output directory.
-
- def update_output_dir(op_dir, time, last = {})
- return if @options.dry_run or not @options.update_output_dir
- unless ENV['SOURCE_DATE_EPOCH'].nil?
- time = Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime
- end
+ def update_output_dir(op_dir, time, last = {})
+ return if @options.dry_run or not @options.update_output_dir
+ unless ENV['SOURCE_DATE_EPOCH'].nil?
+ time = Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime
+ end
- File.open output_flag_file(op_dir), "w" do |f|
- f.puts time.rfc2822
- last.each do |n, t|
- f.puts "#{n}\t#{t.rfc2822}"
+ File.open output_flag_file(op_dir), "w" do |f|
+ f.puts time.rfc2822
+ last.each do |n, t|
+ f.puts "#{n}\t#{t.rfc2822}"
+ end
end
end
- end
- ##
- # Return the path name of the flag file in an output directory.
+ ##
+ # Return the path name of the flag file in an output directory.
- def output_flag_file(op_dir)
- File.join op_dir, "created.rid"
- end
+ def output_flag_file(op_dir)
+ File.join op_dir, "created.rid"
+ end
- ##
- # The .document file contains a list of file and directory name patterns,
- # representing candidates for documentation. It may also contain comments
- # (starting with '#')
+ ##
+ # The .document file contains a list of file and directory name patterns,
+ # representing candidates for documentation. It may also contain comments
+ # (starting with '#')
- def parse_dot_doc_file(in_dir, filename)
- # read and strip comments
- patterns = File.read(filename).gsub(/#.*/, '')
+ def parse_dot_doc_file(in_dir, filename)
+ # read and strip comments
+ patterns = File.read(filename).gsub(/#.*/, '')
- result = {}
+ result = {}
- patterns.split(' ').each do |patt|
- candidates = Dir.glob(File.join(in_dir, patt))
- result.update normalized_file_list(candidates, false, @options.exclude)
- end
+ patterns.split(' ').each do |patt|
+ candidates = Dir.glob(File.join(in_dir, patt))
+ result.update normalized_file_list(candidates, false, @options.exclude)
+ end
- result
- end
+ result
+ end
- ##
- # Given a list of files and directories, create a list of all the Ruby
- # files they contain.
- #
- # If +force_doc+ is true we always add the given files, if false, only
- # add files that we guarantee we can parse. It is true when looking at
- # files given on the command line, false when recursing through
- # subdirectories.
- #
- # The effect of this is that if you want a file with a non-standard
- # extension parsed, you must name it explicitly.
-
- def normalized_file_list(relative_files, force_doc = false,
- exclude_pattern = nil)
- file_list = {}
-
- relative_files.each do |rel_file_name|
- rel_file_name = rel_file_name.sub(/^\.\//, '')
- next if rel_file_name.end_with? 'created.rid'
- next if exclude_pattern && exclude_pattern =~ rel_file_name
- stat = File.stat rel_file_name rescue next
-
- case type = stat.ftype
- when "file"
- mtime = (stat.mtime unless (last_modified = @last_modified[rel_file_name] and
- stat.mtime.to_i <= last_modified.to_i))
-
- if force_doc or RDoc::Parser.can_parse(rel_file_name)
- file_list[rel_file_name] = mtime
- end
- when "directory"
- next if UNCONDITIONALLY_SKIPPED_DIRECTORIES.include?(rel_file_name)
+ ##
+ # Given a list of files and directories, create a list of all the Ruby
+ # files they contain.
+ #
+ # If +force_doc+ is true we always add the given files, if false, only
+ # add files that we guarantee we can parse. It is true when looking at
+ # files given on the command line, false when recursing through
+ # subdirectories.
+ #
+ # The effect of this is that if you want a file with a non-standard
+ # extension parsed, you must name it explicitly.
+
+ def normalized_file_list(relative_files, force_doc = false,
+ exclude_pattern = nil)
+ file_list = {}
+
+ relative_files.each do |rel_file_name|
+ rel_file_name = rel_file_name.sub(/^\.\//, '')
+ next if rel_file_name.end_with? 'created.rid'
+ next if exclude_pattern && exclude_pattern =~ rel_file_name
+ stat = File.stat rel_file_name rescue next
+
+ case type = stat.ftype
+ when "file"
+ mtime = (stat.mtime unless (last_modified = @last_modified[rel_file_name] and
+ stat.mtime.to_i <= last_modified.to_i))
+
+ if force_doc or Parser.can_parse(rel_file_name)
+ file_list[rel_file_name] = mtime
+ end
+ when "directory"
+ next if UNCONDITIONALLY_SKIPPED_DIRECTORIES.include?(rel_file_name)
- basename = File.basename(rel_file_name)
- next if options.skip_tests && TEST_SUITE_DIRECTORY_NAMES.include?(basename)
+ basename = File.basename(rel_file_name)
+ next if options.skip_tests && TEST_SUITE_DIRECTORY_NAMES.include?(basename)
- created_rid = File.join rel_file_name, "created.rid"
- next if File.file? created_rid
+ created_rid = File.join rel_file_name, "created.rid"
+ next if File.file? created_rid
- dot_doc = File.join rel_file_name, RDoc::DOT_DOC_FILENAME
+ dot_doc = File.join rel_file_name, DOT_DOC_FILENAME
- if File.file? dot_doc
- file_list.update(parse_dot_doc_file(rel_file_name, dot_doc))
+ if File.file? dot_doc
+ file_list.update(parse_dot_doc_file(rel_file_name, dot_doc))
+ else
+ file_list.update(list_files_in_directory(rel_file_name))
+ end
else
- file_list.update(list_files_in_directory(rel_file_name))
+ warn "rdoc can't parse the #{type} #{rel_file_name}"
end
- else
- warn "rdoc can't parse the #{type} #{rel_file_name}"
end
- end
- file_list
- end
+ file_list
+ end
- ##
- # Return a list of the files to be processed in a directory. We know that
- # this directory doesn't have a .document file, so we're looking for real
- # files. However we may well contain subdirectories which must be tested
- # for .document files.
+ ##
+ # Return a list of the files to be processed in a directory. We know that
+ # this directory doesn't have a .document file, so we're looking for real
+ # files. However we may well contain subdirectories which must be tested
+ # for .document files.
- def list_files_in_directory(dir)
- files = Dir.glob File.join(dir, "*")
+ def list_files_in_directory(dir)
+ files = Dir.glob File.join(dir, "*")
- normalized_file_list files, false, @options.exclude
- end
+ normalized_file_list files, false, @options.exclude
+ end
- ##
- # Parses +filename+ and returns an RDoc::TopLevel
+ ##
+ # Parses +filename+ and returns an RDoc::TopLevel
- def parse_file(filename)
- encoding = @options.encoding
- filename = filename.encode encoding
+ def parse_file(filename)
+ encoding = @options.encoding
+ filename = filename.encode encoding
- @stats.add_file filename
+ @stats.add_file filename
- return if RDoc::Parser.binary? filename
+ return if Parser.binary? filename
- content = RDoc::Encoding.read_file filename, encoding
+ content = Encoding.read_file filename, encoding
- return unless content
+ return unless content
- top_level = @store.add_file filename, relative_name: relative_path_for(filename)
+ top_level = @store.add_file filename, relative_name: relative_path_for(filename)
- parser = RDoc::Parser.for top_level, content, @options, @stats
+ parser = Parser.for top_level, content, @options, @stats
- return unless parser
+ return unless parser
- parser.scan
+ parser.scan
- # restart documentation for the classes & modules found
- top_level.classes_or_modules.each do |cm|
- cm.done_documenting = false
- end
+ # restart documentation for the classes & modules found
+ top_level.classes_or_modules.each do |cm|
+ cm.done_documenting = false
+ end
- top_level
+ top_level
- rescue Errno::EACCES => e
- $stderr.puts <<-EOF
+ rescue Errno::EACCES => e
+ $stderr.puts <<-EOF
Unable to read #{filename}, #{e.message}
Please check the permissions for this file. Perhaps you do not have access to
it or perhaps the original author's permissions are to restrictive. If the
this is not your library please report a bug to the author.
EOF
- rescue => e
- syntax_check_command = syntax_check_command_for filename, parser&.class
- syntax_check_message = if syntax_check_command
- <<~MESSAGE
+ rescue => e
+ syntax_check_command = syntax_check_command_for filename, parser&.class
+ syntax_check_message = if syntax_check_command
+ <<~MESSAGE
Before reporting this, could you check that the file you're documenting
has proper syntax:
#{syntax_check_command}
MESSAGE
- else
- <<~MESSAGE
+ else
+ <<~MESSAGE
Before reporting this, could you check that the file you're documenting
has proper syntax for its language?
MESSAGE
- end
+ end
- $stderr.puts <<-EOF
+ $stderr.puts <<-EOF
#{syntax_check_message}
RDoc's parsers are not full language parsers and may fail when fed invalid
source files.
@@ -383,323 +384,324 @@ def parse_file(filename)
EOF
- $stderr.puts e.backtrace.join("\n\t") if $DEBUG_RDOC
+ $stderr.puts e.backtrace.join("\n\t") if $DEBUG_RDOC
- raise e
- end
-
- def syntax_check_command_for(filename, parser_class = RDoc::Parser.can_parse_by_name(filename))
- if parser_class == RDoc::Parser::Ruby
- "#{Gem.ruby} -c #{filename}"
- elsif parser_class == RDoc::Parser::C
- cc = ENV['CC']
- cc = 'cc' if cc.nil? || cc.empty?
- "#{cc} -fsyntax-only #{filename}"
+ raise e
end
- end
-
- ##
- # Returns the relative path for +filename+ against +options.root+ (and
- # +options.page_dir+ when set). This is the key used by RDoc::Store to
- # identify files.
- def relative_path_for(filename)
- filename_path = Pathname(filename).expand_path
- begin
- relative_path = filename_path.relative_path_from @options.root
- rescue ArgumentError
- relative_path = filename_path
+ def syntax_check_command_for(filename, parser_class = Parser.can_parse_by_name(filename))
+ if parser_class == Parser::Ruby
+ "#{Gem.ruby} -c #{filename}"
+ elsif parser_class == Parser::C
+ cc = ENV['CC']
+ cc = 'cc' if cc.nil? || cc.empty?
+ "#{cc} -fsyntax-only #{filename}"
+ end
end
- if @options.page_dir &&
- relative_path.to_s.start_with?(@options.page_dir.to_s)
- relative_path =
- relative_path.relative_path_from @options.page_dir
- end
+ ##
+ # Returns the relative path for +filename+ against +options.root+ (and
+ # +options.page_dir+ when set). This is the key used by RDoc::Store to
+ # identify files.
- relative_path.to_s
- end
+ def relative_path_for(filename)
+ filename_path = Pathname(filename).expand_path
+ begin
+ relative_path = filename_path.relative_path_from @options.root
+ rescue ArgumentError
+ relative_path = filename_path
+ end
- ##
- # Parse each file on the command line, recursively entering directories.
+ if @options.page_dir &&
+ relative_path.to_s.start_with?(@options.page_dir.to_s)
+ relative_path =
+ relative_path.relative_path_from @options.page_dir
+ end
- def parse_files(files)
- file_list = gather_files files
- @stats = RDoc::Stats.new @store, file_list.length, @options.verbosity
+ relative_path.to_s
+ end
- return [] if file_list.empty?
+ ##
+ # Parse each file on the command line, recursively entering directories.
- # This workaround can be removed after the :main: directive is removed
- original_options = @options.dup
- @stats.begin_adding
+ def parse_files(files)
+ file_list = gather_files files
+ @stats = Stats.new @store, file_list.length, @options.verbosity
- file_info = file_list.map do |filename|
- @current = filename
- parse_file filename
- end.compact
+ return [] if file_list.empty?
- @store.resolve_c_superclasses
+ # This workaround can be removed after the :main: directive is removed
+ original_options = @options.dup
+ @stats.begin_adding
- @stats.done_adding
- @options = original_options
+ file_info = file_list.map do |filename|
+ @current = filename
+ parse_file filename
+ end.compact
- file_info
- end
+ @store.resolve_c_superclasses
- ##
- # Removes file extensions known to be unparseable from +files+ and TAGS
- # files for emacs and vim.
+ @stats.done_adding
+ @options = original_options
- def remove_unparseable(files)
- files.reject do |file, *|
- file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)\z/i or
- (file =~ /tags\z/i and
- /\A(\f\n[^,]+,\d+$|!_TAG_)/.match?(File.binread(file, 100)))
+ file_info
end
- end
- ##
- # Removes duplicate canonical paths while preserving the first path found.
+ ##
+ # Removes file extensions known to be unparseable from +files+ and TAGS
+ # files for emacs and vim.
- def remove_duplicate_files(files)
- files.uniq { |file,| File.realpath(file) }.to_h
- end
+ def remove_unparseable(files)
+ files.reject do |file, *|
+ file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)\z/i or
+ (file =~ /tags\z/i and
+ /\A(\f\n[^,]+,\d+$|!_TAG_)/.match?(File.binread(file, 100)))
+ end
+ end
- ##
- # Generates documentation or a coverage report depending upon the settings
- # in +options+.
- #
- # +options+ can be either an RDoc::Options instance or an array of strings
- # equivalent to the strings that would be passed on the command line like
- # %w[-q -o doc -t My\ Doc\ Title]. #document will automatically
- # call RDoc::Options#finish if an options instance was given.
- #
- # For a list of options, see either RDoc::Options or rdoc --help.
- #
- # By default, output will be stored in a directory called "doc" below the
- # current directory, so make sure you're somewhere writable before invoking.
+ ##
+ # Removes duplicate canonical paths while preserving the first path found.
- def document(options)
- if RDoc::Options === options
- @options = options
- else
- @options = RDoc::Options.load_options
- @options.parse options
+ def remove_duplicate_files(files)
+ files.uniq { |file,| File.realpath(file) }.to_h
end
- @options.finish
- @store = RDoc::Store.new(@options)
+ ##
+ # Generates documentation or a coverage report depending upon the settings
+ # in +options+.
+ #
+ # +options+ can be either an RDoc::Options instance or an array of strings
+ # equivalent to the strings that would be passed on the command line like
+ # %w[-q -o doc -t My\ Doc\ Title]. #document will automatically
+ # call RDoc::Options#finish if an options instance was given.
+ #
+ # For a list of options, see either RDoc::Options or rdoc --help.
+ #
+ # By default, output will be stored in a directory called "doc" below the
+ # current directory, so make sure you're somewhere writable before invoking.
+
+ def document(options)
+ if Options === options
+ @options = options
+ else
+ @options = Options.load_options
+ @options.parse options
+ end
+ @options.finish
- if @options.pipe
- handle_pipe
- exit
- end
+ @store = Store.new(@options)
- if @options.server_port
- @store.load_cache
+ if @options.pipe
+ handle_pipe
+ exit
+ end
- parse_files @options.files
- record_auto_discovered_rbs_signature_mtimes
+ if @options.server_port
+ @store.load_cache
- @options.default_title = "RDoc Documentation"
+ parse_files @options.files
+ record_auto_discovered_rbs_signature_mtimes
- load_auto_discovered_rbs_signatures
- @store.complete @options.visibility
+ @options.default_title = "RDoc Documentation"
- start_server
- exit
- end
+ load_auto_discovered_rbs_signatures
+ @store.complete @options.visibility
- unless @options.coverage_report
- @last_modified = setup_output_dir @options.op_dir, @options.force_update
- end
+ start_server
+ exit
+ end
- @start_time = Time.now
+ unless @options.coverage_report
+ @last_modified = setup_output_dir @options.op_dir, @options.force_update
+ end
- @store.load_cache
+ @start_time = Time.now
- auto_discovered_rbs_signatures_changed = auto_discovered_rbs_signatures_changed?
- # When only auto-discovered RBS signatures changed, no Ruby file would be
- # reparsed under normal mtime checks. The store cache holds class metadata
- # but not live RDoc::Context objects, so the generator would have nothing
- # to iterate. Force a full reparse so updated signatures show up in the
- # rendered output.
- @last_modified.clear if auto_discovered_rbs_signatures_changed
+ @store.load_cache
- file_info = parse_files @options.files
- record_auto_discovered_rbs_signature_mtimes
+ auto_discovered_rbs_signatures_changed = auto_discovered_rbs_signatures_changed?
+ # When only auto-discovered RBS signatures changed, no Ruby file would be
+ # reparsed under normal mtime checks. The store cache holds class metadata
+ # but not live RDoc::Context objects, so the generator would have nothing
+ # to iterate. Force a full reparse so updated signatures show up in the
+ # rendered output.
+ @last_modified.clear if auto_discovered_rbs_signatures_changed
- @options.default_title = "RDoc Documentation"
+ file_info = parse_files @options.files
+ record_auto_discovered_rbs_signature_mtimes
- load_auto_discovered_rbs_signatures
+ @options.default_title = "RDoc Documentation"
- @store.complete @options.visibility
+ load_auto_discovered_rbs_signatures
- @stats.coverage_level = @options.coverage_report
+ @store.complete @options.visibility
- if @options.coverage_report
- puts
+ @stats.coverage_level = @options.coverage_report
- puts @stats.report
- elsif file_info.empty? && !auto_discovered_rbs_signatures_changed
- $stderr.puts "\nNo newer files." unless @options.quiet
- else
- gen_klass = @options.generator
+ if @options.coverage_report
+ puts
- @generator = gen_klass.new @store, @options
+ puts @stats.report
+ elsif file_info.empty? && !auto_discovered_rbs_signatures_changed
+ $stderr.puts "\nNo newer files." unless @options.quiet
+ else
+ gen_klass = @options.generator
- generate
- end
+ @generator = gen_klass.new @store, @options
- if @stats and (@options.coverage_report or not @options.quiet)
- puts
- puts @stats.summary
- end
+ generate
+ end
- exit @stats.fully_documented? if @options.coverage_report
- end
+ if @stats and (@options.coverage_report or not @options.quiet)
+ puts
+ puts @stats.summary
+ end
- ##
- # Generates documentation for +file_info+ (from #parse_files) into the
- # output dir using the generator selected
- # by the RDoc options
-
- def generate
- if @options.dry_run
- # do nothing
- @generator.generate
- else
- Dir.chdir @options.op_dir do
- unless @options.quiet
- $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
- uri = "file://#{Dir.pwd}/index.html"
- ref = $stderr.tty? ? "\e]8;;#{uri}\e\\#{uri}\e]8;;\e\\" : uri
- $stderr.puts "\nYou can visit the home page at: #{ref}"
- end
+ exit @stats.fully_documented? if @options.coverage_report
+ end
+
+ ##
+ # Generates documentation for +file_info+ (from #parse_files) into the
+ # output dir using the generator selected
+ # by the RDoc options
+ def generate
+ if @options.dry_run
+ # do nothing
@generator.generate
- update_output_dir '.', @start_time, @last_modified
+ else
+ Dir.chdir @options.op_dir do
+ unless @options.quiet
+ $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
+ uri = "file://#{Dir.pwd}/index.html"
+ ref = $stderr.tty? ? "\e]8;;#{uri}\e\\#{uri}\e]8;;\e\\" : uri
+ $stderr.puts "\nYou can visit the home page at: #{ref}"
+ end
+
+ @generator.generate
+ update_output_dir '.', @start_time, @last_modified
+ end
end
end
- end
- ##
- # Loads RBS type signatures from the project's +sig+ directory and RBS
- # stdlib, then merges them into the store's code objects.
-
- def load_auto_discovered_rbs_signatures
- sig_dirs = []
- sig_dir = File.join(@options.root.to_s, 'sig')
- sig_dirs << sig_dir if File.directory?(sig_dir)
- signatures = RDoc::RbsHelper.load_signatures(*sig_dirs)
- @store.merge_rbs_signatures(signatures)
- rescue RBS::BaseError, Errno::ENOENT, LoadError => e
- # In server mode, a previous successful load may have populated the store;
- # drop those signatures so a now-broken sig file doesn't keep showing
- # stale types alongside the warning.
- @store.clear_rbs_signatures
- @options.warn "Failed to load RBS type signatures: #{e.message}"
- end
+ ##
+ # Loads RBS type signatures from the project's +sig+ directory and RBS
+ # stdlib, then merges them into the store's code objects.
+
+ def load_auto_discovered_rbs_signatures
+ sig_dirs = []
+ sig_dir = File.join(@options.root.to_s, 'sig')
+ sig_dirs << sig_dir if File.directory?(sig_dir)
+ signatures = RbsHelper.load_signatures(*sig_dirs)
+ @store.merge_rbs_signatures(signatures)
+ rescue RBS::BaseError, Errno::ENOENT, LoadError => e
+ # In server mode, a previous successful load may have populated the store;
+ # drop those signatures so a now-broken sig file doesn't keep showing
+ # stale types alongside the warning.
+ @store.clear_rbs_signatures
+ @options.warn "Failed to load RBS type signatures: #{e.message}"
+ end
- ##
- # Returns RBS files that RDoc auto-discovers for signature loading.
+ ##
+ # Returns RBS files that RDoc auto-discovers for signature loading.
- def auto_discovered_rbs_signature_files
- Dir[File.join(@options.root.to_s, 'sig', '**', '*.rbs')].sort
- end
+ def auto_discovered_rbs_signature_files
+ Dir[File.join(@options.root.to_s, 'sig', '**', '*.rbs')].sort
+ end
- ##
- # Returns true if any auto-discovered RBS signature file has changed since
- # the last run.
+ ##
+ # Returns true if any auto-discovered RBS signature file has changed since
+ # the last run.
- def auto_discovered_rbs_signatures_changed?
- current = auto_discovered_rbs_signature_mtimes
- previous = @last_modified.select { |file, _| auto_discovered_rbs_signature_file?(file) }
+ def auto_discovered_rbs_signatures_changed?
+ current = auto_discovered_rbs_signature_mtimes
+ previous = @last_modified.select { |file, _| auto_discovered_rbs_signature_file?(file) }
- return true unless (previous.keys - current.keys).empty?
+ return true unless (previous.keys - current.keys).empty?
- current.any? do |file, mtime|
- last_modified = @last_modified[file]
- last_modified.nil? || mtime.to_i > last_modified.to_i
+ current.any? do |file, mtime|
+ last_modified = @last_modified[file]
+ last_modified.nil? || mtime.to_i > last_modified.to_i
+ end
end
- end
- ##
- # Records auto-discovered RBS signature file mtimes so normal generation
- # freshness checks and the live server watcher can see signature-only edits.
+ ##
+ # Records auto-discovered RBS signature file mtimes so normal generation
+ # freshness checks and the live server watcher can see signature-only edits.
- def record_auto_discovered_rbs_signature_mtimes
- @last_modified.reject! { |file, _| auto_discovered_rbs_signature_file?(file) }
- @last_modified.merge! auto_discovered_rbs_signature_mtimes
- end
+ def record_auto_discovered_rbs_signature_mtimes
+ @last_modified.reject! { |file, _| auto_discovered_rbs_signature_file?(file) }
+ @last_modified.merge! auto_discovered_rbs_signature_mtimes
+ end
- ##
- # Files watched by the live preview server.
+ ##
+ # Files watched by the live preview server.
- def watch_files
- (@last_modified.keys + auto_discovered_rbs_signature_files).uniq
- end
+ def watch_files
+ (@last_modified.keys + auto_discovered_rbs_signature_files).uniq
+ end
- ##
- # Returns true for project RBS files that are auto-discovered for signature
- # loading. RDoc parses any selected .rbs file as documentation input, but
- # only +sig/**/*.rbs+ files are loaded through RBS::EnvironmentLoader for
- # type signature merging and live-reload bookkeeping.
-
- def auto_discovered_rbs_signature_file?(file) # :nodoc:
- return false unless File.extname(file) == '.rbs'
-
- root = Pathname(@options.root.to_s).expand_path
- relative_path = Pathname(file).expand_path.relative_path_from root
- relative_path.each_filename.first == 'sig'
- rescue ArgumentError
- # file and root may be on different drives on Windows
- false
- end
+ ##
+ # Returns true for project RBS files that are auto-discovered for signature
+ # loading. RDoc parses any selected .rbs file as documentation input, but
+ # only +sig/**/*.rbs+ files are loaded through RBS::EnvironmentLoader for
+ # type signature merging and live-reload bookkeeping.
- ##
- # Returns mtimes for auto-discovered RBS signature files.
+ def auto_discovered_rbs_signature_file?(file) # :nodoc:
+ return false unless File.extname(file) == '.rbs'
- def auto_discovered_rbs_signature_mtimes # :nodoc:
- auto_discovered_rbs_signature_files.each_with_object({}) do |file, mtimes|
- mtime = RDoc.safe_mtime(file)
- mtimes[file] = mtime if mtime
+ root = Pathname(@options.root.to_s).expand_path
+ relative_path = Pathname(file).expand_path.relative_path_from root
+ relative_path.each_filename.first == 'sig'
+ rescue ArgumentError
+ # file and root may be on different drives on Windows
+ false
end
- end
- ##
- # Starts a live-reloading HTTP server for previewing documentation.
- # Called from #document when --server is given.
+ ##
+ # Returns mtimes for auto-discovered RBS signature files.
- def start_server
- server = RDoc::Server.new(self, @options.server_port)
- server.start
- end
+ def auto_discovered_rbs_signature_mtimes # :nodoc:
+ auto_discovered_rbs_signature_files.each_with_object({}) do |file, mtimes|
+ mtime = ::RDoc.safe_mtime(file)
+ mtimes[file] = mtime if mtime
+ end
+ end
- ##
- # Removes a siginfo handler and replaces the previous
+ ##
+ # Starts a live-reloading HTTP server for previewing documentation.
+ # Called from #document when --server is given.
- def remove_siginfo_handler
- return unless Signal.list.key? 'INFO'
+ def start_server
+ server = Server.new(self, @options.server_port)
+ server.start
+ end
- handler = @old_siginfo || 'DEFAULT'
+ ##
+ # Removes a siginfo handler and replaces the previous
- trap 'INFO', handler
- end
+ def remove_siginfo_handler
+ return unless Signal.list.key? 'INFO'
- ##
- # Returns true when +extension+ is the RBS gem's RDoc discovery hook.
- # Released RBS gems install their plugin through this hook, so skip it to
- # avoid replacing the built-in parser during discovery.
+ handler = @old_siginfo || 'DEFAULT'
- def self.rbs_discovery_extension?(extension) # :nodoc:
- extension = File.expand_path(extension)
+ trap 'INFO', handler
+ end
- Gem::Specification.find_all_by_name('rbs').any? do |spec|
- File.expand_path('lib/rdoc/discover.rb', spec.full_gem_path) == extension
+ ##
+ # Returns true when +extension+ is the RBS gem's RDoc discovery hook.
+ # Released RBS gems install their plugin through this hook, so skip it to
+ # avoid replacing the built-in parser during discovery.
+
+ def self.rbs_discovery_extension?(extension) # :nodoc:
+ extension = File.expand_path(extension)
+
+ Gem::Specification.find_all_by_name('rbs').any? do |spec|
+ File.expand_path('lib/rdoc/discover.rb', spec.full_gem_path) == extension
+ end
end
- end
+ end
end
# Load built-in parser registrations before RubyGems discovery, then skip the
diff --git a/lib/rdoc/ri.rb b/lib/rdoc/ri.rb
index ccf11c4636..b8c6683c1e 100644
--- a/lib/rdoc/ri.rb
+++ b/lib/rdoc/ri.rb
@@ -1,21 +1,23 @@
# frozen_string_literal: true
require_relative '../rdoc'
-##
-# Namespace for the ri command line tool's implementation.
-#
-# See ri --help for details.
+module RDoc
+ ##
+ # Namespace for the ri command line tool's implementation.
+ #
+ # See ri --help for details.
-module RDoc::RI
+ module RI
- ##
- # Base RI error class
+ ##
+ # Base RI error class
- class Error < RDoc::Error; end
+ class Error < ::RDoc::Error; end
- autoload :Driver, "#{__dir__}/ri/driver"
- autoload :Paths, "#{__dir__}/ri/paths"
- autoload :Servlet, "#{__dir__}/ri/servlet"
- autoload :Store, "#{__dir__}/ri/store"
+ autoload :Driver, "#{__dir__}/ri/driver"
+ autoload :Paths, "#{__dir__}/ri/paths"
+ autoload :Servlet, "#{__dir__}/ri/servlet"
+ autoload :Store, "#{__dir__}/ri/store"
+ end
end
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index 3882ee3c08..759430fa98 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -6,117 +6,119 @@
require_relative 'formatter' # For RubyGems backwards compatibility
# TODO: Fix weird documentation with `require_relative`
-##
-# The RI driver implements the command-line ri tool.
-#
-# The driver supports:
-# * loading RI data from:
-# * Ruby's standard library
-# * RubyGems
-# * ~/.rdoc
-# * A user-supplied directory
-# * Paging output (uses RI_PAGER environment variable, PAGER environment
-# variable or the less, more and pager programs)
-# * Interactive mode with tab-completion
-# * Abbreviated names (ri Zl shows Zlib documentation)
-# * Colorized output
-# * Merging output from multiple RI data sources
-
-class RDoc::RI::Driver
-
- ##
- # Base Driver error class
-
- class Error < RDoc::RI::Error; end
-
- ##
- # Raised when a name isn't found in the ri data stores
-
- class NotFoundError < Error
-
- def initialize(klass, suggestion_proc = nil) # :nodoc:
- @klass = klass
- @suggestion_proc = suggestion_proc
- end
-
+module RDoc
+ module RI
##
- # Name that wasn't found
+ # The RI driver implements the command-line ri tool.
+ #
+ # The driver supports:
+ # * loading RI data from:
+ # * Ruby's standard library
+ # * RubyGems
+ # * ~/.rdoc
+ # * A user-supplied directory
+ # * Paging output (uses RI_PAGER environment variable, PAGER environment
+ # variable or the less, more and pager programs)
+ # * Interactive mode with tab-completion
+ # * Abbreviated names (ri Zl shows Zlib documentation)
+ # * Colorized output
+ # * Merging output from multiple RI data sources
+
+ class Driver
+
+ ##
+ # Base Driver error class
+
+ class Error < RI::Error; end
+
+ ##
+ # Raised when a name isn't found in the ri data stores
+
+ class NotFoundError < Error
+
+ def initialize(klass, suggestion_proc = nil) # :nodoc:
+ @klass = klass
+ @suggestion_proc = suggestion_proc
+ end
- def name
- @klass
- end
+ ##
+ # Name that wasn't found
+
+ def name
+ @klass
+ end
- def message # :nodoc:
- str = "Nothing known about #{@klass}"
- suggestions = @suggestion_proc&.call
- if suggestions and !suggestions.empty?
- str += "\nDid you mean? #{suggestions.join("\n ")}"
+ def message # :nodoc:
+ str = "Nothing known about #{@klass}"
+ suggestions = @suggestion_proc&.call
+ if suggestions and !suggestions.empty?
+ str += "\nDid you mean? #{suggestions.join("\n ")}"
+ end
+ str
+ end
end
- str
- end
- end
- ##
- # Show all method documentation following a class or module
+ ##
+ # Show all method documentation following a class or module
- attr_accessor :show_all
+ attr_accessor :show_all
- ##
- # An RDoc::RI::Store for each entry in the RI path
+ ##
+ # An RDoc::RI::Store for each entry in the RI path
- attr_accessor :stores
+ attr_accessor :stores
- ##
- # Controls the user of the pager vs $stdout
+ ##
+ # Controls the user of the pager vs $stdout
- attr_accessor :use_stdout
+ attr_accessor :use_stdout
- ##
- # Default options for ri
+ ##
+ # Default options for ri
- def self.default_options
- options = {}
- options[:interactive] = false
- options[:profile] = false
- options[:show_all] = false
- options[:expand_refs] = true
- options[:use_stdout] = !$stdout.tty?
- options[:width] = 72
+ def self.default_options
+ options = {}
+ options[:interactive] = false
+ options[:profile] = false
+ options[:show_all] = false
+ options[:expand_refs] = true
+ options[:use_stdout] = !$stdout.tty?
+ options[:width] = 72
- # By default all standard paths are used.
- options[:use_system] = true
- options[:use_site] = true
- options[:use_home] = true
- options[:use_gems] = true
- options[:extra_doc_dirs] = []
+ # By default all standard paths are used.
+ options[:use_system] = true
+ options[:use_site] = true
+ options[:use_home] = true
+ options[:use_gems] = true
+ options[:extra_doc_dirs] = []
- return options
- end
+ return options
+ end
- ##
- # Dump +data_path+ using pp
+ ##
+ # Dump +data_path+ using pp
- def self.dump(data_path)
- require 'pp'
+ def self.dump(data_path)
+ require 'pp'
- File.open data_path, 'rb' do |io|
- pp Marshal.load(io.read)
- end
- end
+ File.open data_path, 'rb' do |io|
+ pp Marshal.load(io.read)
+ end
+ end
- ##
- # Parses +argv+ and returns a Hash of options
+ ##
+ # Parses +argv+ and returns a Hash of options
- def self.process_args(argv)
- options = default_options
+ def self.process_args(argv)
+ options = default_options
- opts = OptionParser.new do |opt|
- opt.program_name = File.basename $0
- opt.version = RDoc::VERSION
- opt.release = nil
- opt.summary_indent = ' ' * 4
+ opts = OptionParser.new do |opt|
+ opt.program_name = File.basename $0
+ opt.version = VERSION
+ opt.release = nil
+ opt.summary_indent = ' ' * 4
- opt.banner = <<-EOT
+ opt.banner = <<-EOT
Usage: #{opt.program_name} [options] [name ...]
Where name can be:
@@ -172,1401 +174,1403 @@ def self.process_args(argv)
or the PAGER environment variable.
EOT
- opt.separator nil
- opt.separator "Options:"
+ opt.separator nil
+ opt.separator "Options:"
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]interactive", "-i",
- "In interactive mode you can repeatedly",
- "look up methods with autocomplete.") do |interactive|
- options[:interactive] = interactive
- end
+ opt.on("--[no-]interactive", "-i",
+ "In interactive mode you can repeatedly",
+ "look up methods with autocomplete.") do |interactive|
+ options[:interactive] = interactive
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]all", "-a",
- "Show all documentation for a class or",
- "module.") do |show_all|
- options[:show_all] = show_all
- end
+ opt.on("--[no-]all", "-a",
+ "Show all documentation for a class or",
+ "module.") do |show_all|
+ options[:show_all] = show_all
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]list", "-l",
- "List classes ri knows about.") do |list|
- options[:list] = list
- end
+ opt.on("--[no-]list", "-l",
+ "List classes ri knows about.") do |list|
+ options[:list] = list
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]pager",
- "Send output to a pager,",
- "rather than directly to stdout.") do |use_pager|
- options[:use_stdout] = !use_pager
- end
+ opt.on("--[no-]pager",
+ "Send output to a pager,",
+ "rather than directly to stdout.") do |use_pager|
+ options[:use_stdout] = !use_pager
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("-T",
- "Synonym for --no-pager.") do
- options[:use_stdout] = true
- end
+ opt.on("-T",
+ "Synonym for --no-pager.") do
+ options[:use_stdout] = true
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
- "Set the width of the output.") do |width|
- options[:width] = width
- end
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
+ "Set the width of the output.") do |width|
+ options[:width] = width
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--server[=PORT]", Integer,
- "Run RDoc server on the given port.",
- "The default port is 8214.") do |port|
- options[:server] = port || 8214
- end
+ opt.on("--server[=PORT]", Integer,
+ "Run RDoc server on the given port.",
+ "The default port is 8214.") do |port|
+ options[:server] = port || 8214
+ end
- opt.separator nil
+ opt.separator nil
- formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
- formatters = formatters.sort.map do |formatter|
- formatter.to_s.sub('To', '').downcase
- end
- formatters -= %w[html label test] # remove useless output formats
-
- opt.on("--format=NAME", "-f",
- "Use the selected formatter. The default",
- "formatter is bs for paged output and ansi",
- "otherwise. Valid formatters are:",
- "#{formatters.join(', ')}.", formatters) do |value|
- options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
- end
+ formatters = Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
+ formatters = formatters.sort.map do |formatter|
+ formatter.to_s.sub('To', '').downcase
+ end
+ formatters -= %w[html label test] # remove useless output formats
+
+ opt.on("--format=NAME", "-f",
+ "Use the selected formatter. The default",
+ "formatter is bs for paged output and ansi",
+ "otherwise. Valid formatters are:",
+ "#{formatters.join(', ')}.", formatters) do |value|
+ options[:formatter] = Markup.const_get "To#{value.capitalize}"
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--[no-]expand-refs", "Expand rdoc-refs at the end of output") do |value|
- options[:expand_refs] = value
- end
+ opt.on("--[no-]expand-refs", "Expand rdoc-refs at the end of output") do |value|
+ options[:expand_refs] = value
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--help", "-h",
- "Show help and exit.") do
- puts opts
- exit
- end
+ opt.on("--help", "-h",
+ "Show help and exit.") do
+ puts opts
+ exit
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--version", "-v",
- "Output version information and exit.") do
- puts "#{opts.program_name} #{opts.version}"
- exit
- end
+ opt.on("--version", "-v",
+ "Output version information and exit.") do
+ puts "#{opts.program_name} #{opts.version}"
+ exit
+ end
- opt.separator nil
- opt.separator "Data source options:"
- opt.separator nil
+ opt.separator nil
+ opt.separator "Data source options:"
+ opt.separator nil
- opt.on("--[no-]list-doc-dirs",
- "List the directories from which ri will",
- "source documentation on stdout and exit.") do |list_doc_dirs|
- options[:list_doc_dirs] = list_doc_dirs
- end
+ opt.on("--[no-]list-doc-dirs",
+ "List the directories from which ri will",
+ "source documentation on stdout and exit.") do |list_doc_dirs|
+ options[:list_doc_dirs] = list_doc_dirs
+ end
- opt.separator nil
+ opt.separator nil
- opt.on("--doc-dir=DIRNAME", "-d", Array,
- "List of directories from which to source",
- "documentation in addition to the standard",
- "directories. May be repeated.") do |value|
- value.each do |dir|
- unless File.directory? dir
- raise OptionParser::InvalidArgument, "#{dir} is not a directory"
+ opt.on("--doc-dir=DIRNAME", "-d", Array,
+ "List of directories from which to source",
+ "documentation in addition to the standard",
+ "directories. May be repeated.") do |value|
+ value.each do |dir|
+ unless File.directory? dir
+ raise OptionParser::InvalidArgument, "#{dir} is not a directory"
+ end
+
+ options[:extra_doc_dirs] << File.expand_path(dir)
+ end
end
- options[:extra_doc_dirs] << File.expand_path(dir)
- end
- end
+ opt.separator nil
+
+ opt.on("--no-standard-docs",
+ "Do not include documentation from",
+ "the Ruby standard library, site_lib,",
+ "installed gems, or ~/.rdoc.",
+ "Use with --doc-dir.") do
+ options[:use_system] = false
+ options[:use_site] = false
+ options[:use_gems] = false
+ options[:use_home] = false
+ end
- opt.separator nil
-
- opt.on("--no-standard-docs",
- "Do not include documentation from",
- "the Ruby standard library, site_lib,",
- "installed gems, or ~/.rdoc.",
- "Use with --doc-dir.") do
- options[:use_system] = false
- options[:use_site] = false
- options[:use_gems] = false
- options[:use_home] = false
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--[no-]system",
+ "Include documentation from Ruby's",
+ "standard library. Defaults to true.") do |value|
+ options[:use_system] = value
+ end
- opt.on("--[no-]system",
- "Include documentation from Ruby's",
- "standard library. Defaults to true.") do |value|
- options[:use_system] = value
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--[no-]site",
+ "Include documentation from libraries",
+ "installed in site_lib.",
+ "Defaults to true.") do |value|
+ options[:use_site] = value
+ end
- opt.on("--[no-]site",
- "Include documentation from libraries",
- "installed in site_lib.",
- "Defaults to true.") do |value|
- options[:use_site] = value
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--[no-]gems",
+ "Include documentation from RubyGems.",
+ "Defaults to true.") do |value|
+ options[:use_gems] = value
+ end
- opt.on("--[no-]gems",
- "Include documentation from RubyGems.",
- "Defaults to true.") do |value|
- options[:use_gems] = value
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--[no-]home",
+ "Include documentation stored in ~/.rdoc.",
+ "Defaults to true.") do |value|
+ options[:use_home] = value
+ end
- opt.on("--[no-]home",
- "Include documentation stored in ~/.rdoc.",
- "Defaults to true.") do |value|
- options[:use_home] = value
- end
+ opt.separator nil
+ opt.separator "Debug options:"
+ opt.separator nil
- opt.separator nil
- opt.separator "Debug options:"
- opt.separator nil
+ opt.on("--[no-]profile",
+ "Run with the ruby profiler.") do |value|
+ options[:profile] = value
+ end
- opt.on("--[no-]profile",
- "Run with the ruby profiler.") do |value|
- options[:profile] = value
- end
+ opt.separator nil
- opt.separator nil
+ opt.on("--dump=CACHE",
+ "Dump data from an ri cache or data file.") do |value|
+ unless File.readable?(value)
+ abort "#{value.inspect} is not readable"
+ end
- opt.on("--dump=CACHE",
- "Dump data from an ri cache or data file.") do |value|
- unless File.readable?(value)
- abort "#{value.inspect} is not readable"
- end
+ if File.directory?(value)
+ abort "#{value.inspect} is a directory"
+ end
- if File.directory?(value)
- abort "#{value.inspect} is a directory"
+ options[:dump_path] = File.new(value)
+ end
end
- options[:dump_path] = File.new(value)
- end
- end
-
- argv = ENV['RI'].to_s.split(' ').concat argv
+ argv = ENV['RI'].to_s.split(' ').concat argv
- opts.parse! argv
+ opts.parse! argv
- options[:names] = argv
+ options[:names] = argv
- options[:use_stdout] ||= !$stdout.tty?
- options[:use_stdout] ||= options[:interactive]
- options[:width] ||= 72
+ options[:use_stdout] ||= !$stdout.tty?
+ options[:use_stdout] ||= options[:interactive]
+ options[:width] ||= 72
- options
+ options
- rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
- puts opts
- puts
- puts e
- exit 1
- end
+ rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
+ puts opts
+ puts
+ puts e
+ exit 1
+ end
- ##
- # Runs the ri command line executable using +argv+
+ ##
+ # Runs the ri command line executable using +argv+
- def self.run(argv = ARGV)
- options = process_args argv
+ def self.run(argv = ARGV)
+ options = process_args argv
- if options[:dump_path]
- dump options[:dump_path]
- return
- end
+ if options[:dump_path]
+ dump options[:dump_path]
+ return
+ end
- ri = new options
- ri.run
- end
+ ri = new options
+ ri.run
+ end
- ##
- # Creates a new driver using +initial_options+ from ::process_args
+ ##
+ # Creates a new driver using +initial_options+ from ::process_args
- def initialize(initial_options = {})
- @paging = false
- @classes = nil
+ def initialize(initial_options = {})
+ @paging = false
+ @classes = nil
- options = self.class.default_options.update(initial_options)
+ options = self.class.default_options.update(initial_options)
- @formatter_klass = options[:formatter]
+ @formatter_klass = options[:formatter]
- require 'profile' if options[:profile]
+ require 'profile' if options[:profile]
- @names = options[:names]
- @list = options[:list]
+ @names = options[:names]
+ @list = options[:list]
- @doc_dirs = []
- @stores = []
+ @doc_dirs = []
+ @stores = []
- RDoc::RI::Paths.each(options[:use_system], options[:use_site],
- options[:use_home], options[:use_gems],
- *options[:extra_doc_dirs]) do |path, type|
- @doc_dirs << path
+ RI::Paths.each(options[:use_system], options[:use_site],
+ options[:use_home], options[:use_gems],
+ *options[:extra_doc_dirs]) do |path, type|
+ @doc_dirs << path
- store = RDoc::RI::Store.new(RDoc::Options.new, path: path, type: type)
- store.load_cache
- @stores << store
- end
+ store = RI::Store.new(Options.new, path: path, type: type)
+ store.load_cache
+ @stores << store
+ end
- @list_doc_dirs = options[:list_doc_dirs]
+ @list_doc_dirs = options[:list_doc_dirs]
- @interactive = options[:interactive]
- @server = options[:server]
- @use_stdout = options[:use_stdout]
- @show_all = options[:show_all]
- @width = options[:width]
- @expand_refs = options[:expand_refs]
- end
+ @interactive = options[:interactive]
+ @server = options[:server]
+ @use_stdout = options[:use_stdout]
+ @show_all = options[:show_all]
+ @width = options[:width]
+ @expand_refs = options[:expand_refs]
+ end
- ##
- # Adds paths for undocumented classes +also_in+ to +out+
+ ##
+ # Adds paths for undocumented classes +also_in+ to +out+
- def add_also_in(out, also_in)
- return if also_in.empty?
+ def add_also_in(out, also_in)
+ return if also_in.empty?
- out << RDoc::Markup::Rule.new(1)
- out << RDoc::Markup::Paragraph.new("Also found in:")
+ out << Markup::Rule.new(1)
+ out << Markup::Paragraph.new("Also found in:")
- paths = RDoc::Markup::Verbatim.new
- also_in.each do |store|
- paths.parts.push store.friendly_path, "\n"
- end
- out << paths
- end
+ paths = Markup::Verbatim.new
+ also_in.each do |store|
+ paths.parts.push store.friendly_path, "\n"
+ end
+ out << paths
+ end
- ##
- # Adds a class header to +out+ for class +name+ which is described in
- # +classes+.
+ ##
+ # Adds a class header to +out+ for class +name+ which is described in
+ # +classes+.
- def add_class(out, name, classes)
- heading = if classes.all? { |klass| klass.module? }
- name
- else
- superclass = classes.map do |klass|
- klass.superclass unless klass.module?
- end.compact.shift || 'Object'
+ def add_class(out, name, classes)
+ heading = if classes.all? { |klass| klass.module? }
+ name
+ else
+ superclass = classes.map do |klass|
+ klass.superclass unless klass.module?
+ end.compact.shift || 'Object'
- superclass = superclass.full_name unless String === superclass
+ superclass = superclass.full_name unless String === superclass
- "#{name} < #{superclass}"
- end
+ "#{name} < #{superclass}"
+ end
- out << RDoc::Markup::Heading.new(1, heading)
- out << RDoc::Markup::BlankLine.new
- end
+ out << Markup::Heading.new(1, heading)
+ out << Markup::BlankLine.new
+ end
- ##
- # Adds "(from ...)" to +out+ for +store+
+ ##
+ # Adds "(from ...)" to +out+ for +store+
- def add_from(out, store)
- out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
- end
+ def add_from(out, store)
+ out << Markup::Paragraph.new("(from #{store.friendly_path})")
+ end
- ##
- # Adds +extends+ to +out+
+ ##
+ # Adds +extends+ to +out+
- def add_extends(out, extends)
- add_extension_modules out, 'Extended by', extends
- end
+ def add_extends(out, extends)
+ add_extension_modules out, 'Extended by', extends
+ end
- ##
- # Adds a list of +extensions+ to this module of the given +type+ to +out+.
- # add_includes and add_extends call this, so you should use those directly.
+ ##
+ # Adds a list of +extensions+ to this module of the given +type+ to +out+.
+ # add_includes and add_extends call this, so you should use those directly.
- def add_extension_modules(out, type, extensions)
- return if extensions.empty?
+ def add_extension_modules(out, type, extensions)
+ return if extensions.empty?
- out << RDoc::Markup::Rule.new(1)
- out << RDoc::Markup::Heading.new(1, "#{type}:")
+ out << Markup::Rule.new(1)
+ out << Markup::Heading.new(1, "#{type}:")
- extensions.each do |modules, store|
- if modules.length == 1
- add_extension_modules_single out, store, modules.first
- else
- add_extension_modules_multiple out, store, modules
+ extensions.each do |modules, store|
+ if modules.length == 1
+ add_extension_modules_single out, store, modules.first
+ else
+ add_extension_modules_multiple out, store, modules
+ end
+ end
end
- end
- end
- ##
- # Renders multiple included +modules+ from +store+ to +out+.
+ ##
+ # Renders multiple included +modules+ from +store+ to +out+.
- def add_extension_modules_multiple(out, store, modules) # :nodoc:
- out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
+ def add_extension_modules_multiple(out, store, modules) # :nodoc:
+ out << Markup::Paragraph.new("(from #{store.friendly_path})")
- wout, with = modules.partition { |incl| incl.comment.empty? }
+ wout, with = modules.partition { |incl| incl.comment.empty? }
- out << RDoc::Markup::BlankLine.new unless with.empty?
+ out << Markup::BlankLine.new unless with.empty?
- with.each do |incl|
- out << RDoc::Markup::Paragraph.new(incl.name)
- out << RDoc::Markup::BlankLine.new
- out << incl.comment.parse
- end
+ with.each do |incl|
+ out << Markup::Paragraph.new(incl.name)
+ out << Markup::BlankLine.new
+ out << incl.comment.parse
+ end
- unless wout.empty?
- verb = RDoc::Markup::Verbatim.new
+ unless wout.empty?
+ verb = Markup::Verbatim.new
- wout.each do |incl|
- verb.push incl.name, "\n"
- end
+ wout.each do |incl|
+ verb.push incl.name, "\n"
+ end
- out << verb
- end
- end
+ out << verb
+ end
+ end
- ##
- # Adds a single extension module +include+ from +store+ to +out+
+ ##
+ # Adds a single extension module +include+ from +store+ to +out+
- def add_extension_modules_single(out, store, include) # :nodoc:
- name = include.name
- path = store.friendly_path
- out << RDoc::Markup::Paragraph.new("#{name} (from #{path})")
+ def add_extension_modules_single(out, store, include) # :nodoc:
+ name = include.name
+ path = store.friendly_path
+ out << Markup::Paragraph.new("#{name} (from #{path})")
- if include.comment
- out << RDoc::Markup::BlankLine.new
- out << include.comment.parse
- end
- end
+ if include.comment
+ out << Markup::BlankLine.new
+ out << include.comment.parse
+ end
+ end
- ##
- # Adds +includes+ to +out+
+ ##
+ # Adds +includes+ to +out+
- def add_includes(out, includes)
- add_extension_modules out, 'Includes', includes
- end
+ def add_includes(out, includes)
+ add_extension_modules out, 'Includes', includes
+ end
- ##
- # Looks up the method +name+ and adds it to +out+
+ ##
+ # Looks up the method +name+ and adds it to +out+
- def add_method(out, name)
- filtered = lookup_method name
- method_document out, name, filtered
- end
+ def add_method(out, name)
+ filtered = lookup_method name
+ method_document out, name, filtered
+ end
- ##
- # Adds documentation for all methods in +klass+ to +out+
+ ##
+ # Adds documentation for all methods in +klass+ to +out+
- def add_method_documentation(out, klass)
- klass.method_list.each do |method|
- begin
- add_method out, method.full_name
- rescue NotFoundError
- next
+ def add_method_documentation(out, klass)
+ klass.method_list.each do |method|
+ begin
+ add_method out, method.full_name
+ rescue NotFoundError
+ next
+ end
+ end
end
- end
- end
- ##
- # Adds a list of +methods+ to +out+ with a heading of +name+
+ ##
+ # Adds a list of +methods+ to +out+ with a heading of +name+
- def add_method_list(out, methods, name)
- return if methods.empty?
+ def add_method_list(out, methods, name)
+ return if methods.empty?
- out << RDoc::Markup::Heading.new(1, "#{name}:")
- out << RDoc::Markup::BlankLine.new
+ out << Markup::Heading.new(1, "#{name}:")
+ out << Markup::BlankLine.new
- if @use_stdout and !@interactive
- out.concat methods.map { |method|
- RDoc::Markup::Verbatim.new method
- }
- else
- out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', '))
- end
+ if @use_stdout and !@interactive
+ out.concat methods.map { |method|
+ Markup::Verbatim.new method
+ }
+ else
+ out << Markup::IndentedParagraph.new(2, methods.join(', '))
+ end
- out << RDoc::Markup::BlankLine.new
- end
+ out << Markup::BlankLine.new
+ end
- ##
- # Returns ancestor classes of +klass+
+ ##
+ # Returns ancestor classes of +klass+
- def ancestors_of(klass)
- ancestors = []
+ def ancestors_of(klass)
+ ancestors = []
- unexamined = [klass]
- seen = []
+ unexamined = [klass]
+ seen = []
- loop do
- break if unexamined.empty?
- current = unexamined.shift
- seen << current
+ loop do
+ break if unexamined.empty?
+ current = unexamined.shift
+ seen << current
- stores = classes[current]
+ stores = classes[current]
- next unless stores and not stores.empty?
+ next unless stores and not stores.empty?
- klasses = stores.flat_map do |store|
- store.ancestors[current] || []
- end.uniq
+ klasses = stores.flat_map do |store|
+ store.ancestors[current] || []
+ end.uniq
- klasses = klasses - seen
+ klasses = klasses - seen
- ancestors.concat klasses
- unexamined.concat klasses
- end
+ ancestors.concat klasses
+ unexamined.concat klasses
+ end
- ancestors.reverse
- end
+ ancestors.reverse
+ end
- ##
- # For RubyGems backwards compatibility
+ ##
+ # For RubyGems backwards compatibility
- def class_cache # :nodoc:
- end
+ def class_cache # :nodoc:
+ end
- ##
- # Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
+ ##
+ # Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
- def class_document(name, found, klasses, includes, extends)
- also_in = []
+ def class_document(name, found, klasses, includes, extends)
+ also_in = []
- out = RDoc::Markup::Document.new
+ out = Markup::Document.new
- add_class out, name, klasses
+ add_class out, name, klasses
- add_includes out, includes
- add_extends out, extends
+ add_includes out, includes
+ add_extends out, extends
- found.each do |store, klass|
- render_class out, store, klass, also_in
- end
+ found.each do |store, klass|
+ render_class out, store, klass, also_in
+ end
- add_also_in out, also_in
+ add_also_in out, also_in
- expand_rdoc_refs_at_the_bottom(out)
- out
- end
+ expand_rdoc_refs_at_the_bottom(out)
+ out
+ end
- ##
- # Adds the class +comment+ to +out+.
+ ##
+ # Adds the class +comment+ to +out+.
- def class_document_comment(out, document) # :nodoc:
- unless document.empty?
- out << RDoc::Markup::Rule.new(1)
+ def class_document_comment(out, document) # :nodoc:
+ unless document.empty?
+ out << Markup::Rule.new(1)
- if document.merged?
- parts = document.parts
- parts = parts.zip [RDoc::Markup::BlankLine.new] * parts.length
- parts.flatten!
- parts.pop
+ if document.merged?
+ parts = document.parts
+ parts = parts.zip [Markup::BlankLine.new] * parts.length
+ parts.flatten!
+ parts.pop
- out.concat parts
- else
- out << comment
+ out.concat parts
+ else
+ out << comment
+ end
+ end
end
- end
- end
- ##
- # Adds the constants from +klass+ to the Document +out+.
+ ##
+ # Adds the constants from +klass+ to the Document +out+.
- def class_document_constants(out, klass) # :nodoc:
- return if klass.constants.empty?
+ def class_document_constants(out, klass) # :nodoc:
+ return if klass.constants.empty?
- out << RDoc::Markup::Heading.new(1, "Constants:")
- out << RDoc::Markup::BlankLine.new
- list = RDoc::Markup::List.new :NOTE
+ out << Markup::Heading.new(1, "Constants:")
+ out << Markup::BlankLine.new
+ list = Markup::List.new :NOTE
- constants = klass.constants.sort_by { |constant| constant.name }
+ constants = klass.constants.sort_by { |constant| constant.name }
- list.items.concat constants.map { |constant|
- parts = constant.comment.parse.parts
- parts << RDoc::Markup::Paragraph.new('[not documented]') if
- parts.empty?
+ list.items.concat constants.map { |constant|
+ parts = constant.comment.parse.parts
+ parts << Markup::Paragraph.new('[not documented]') if
+ parts.empty?
- RDoc::Markup::ListItem.new(constant.name, *parts)
- }
+ Markup::ListItem.new(constant.name, *parts)
+ }
- out << list
- out << RDoc::Markup::BlankLine.new
- end
+ out << list
+ out << Markup::BlankLine.new
+ end
- ##
- # Hash mapping a known class or module to the stores it can be loaded from
+ ##
+ # Hash mapping a known class or module to the stores it can be loaded from
- def classes
- return @classes if @classes
+ def classes
+ return @classes if @classes
- @classes = {}
+ @classes = {}
- @stores.each do |store|
- store.cache[:modules].each do |mod|
- # using default block causes searched-for modules to be added
- @classes[mod] ||= []
- @classes[mod] << store
- end
- end
-
- @classes
- end
+ @stores.each do |store|
+ store.cache[:modules].each do |mod|
+ # using default block causes searched-for modules to be added
+ @classes[mod] ||= []
+ @classes[mod] << store
+ end
+ end
- ##
- # Returns the stores wherein +name+ is found along with the classes,
- # extends and includes that match it
-
- def classes_and_includes_and_extends_for(name)
- klasses = []
- extends = []
- includes = []
-
- found = @stores.map do |store|
- begin
- klass = store.load_class name
- klasses << klass
- extends << [klass.extends, store] if klass.extends
- includes << [klass.includes, store] if klass.includes
- [store, klass]
- rescue RDoc::Store::MissingFileError
+ @classes
end
- end.compact
- extends.reject! do |modules,| modules.empty? end
- includes.reject! do |modules,| modules.empty? end
+ ##
+ # Returns the stores wherein +name+ is found along with the classes,
+ # extends and includes that match it
+
+ def classes_and_includes_and_extends_for(name)
+ klasses = []
+ extends = []
+ includes = []
+
+ found = @stores.map do |store|
+ begin
+ klass = store.load_class name
+ klasses << klass
+ extends << [klass.extends, store] if klass.extends
+ includes << [klass.includes, store] if klass.includes
+ [store, klass]
+ rescue ::RDoc::Store::MissingFileError
+ end
+ end.compact
- [found, klasses, includes, extends]
- end
+ extends.reject! do |modules,| modules.empty? end
+ includes.reject! do |modules,| modules.empty? end
- ##
- # Completes +name+ based on the caches. For Readline
+ [found, klasses, includes, extends]
+ end
- def complete(name)
- completions = []
+ ##
+ # Completes +name+ based on the caches. For Readline
- klass, selector, method = parse_name name
+ def complete(name)
+ completions = []
- complete_klass name, klass, selector, method, completions
- complete_method name, klass, selector, completions
+ klass, selector, method = parse_name name
- completions.uniq.select {|s| s.start_with? name }.sort
- end
+ complete_klass name, klass, selector, method, completions
+ complete_method name, klass, selector, completions
- def complete_klass(name, klass, selector, method, completions) # :nodoc:
- klasses = classes.keys
+ completions.uniq.select {|s| s.start_with? name }.sort
+ end
- # may need to include Foo when given Foo::
- klass_name = method ? name : klass
+ def complete_klass(name, klass, selector, method, completions) # :nodoc:
+ klasses = classes.keys
- if name !~ /#|\./
- completions.replace klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/)
- completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if
- name =~ /::$/
+ # may need to include Foo when given Foo::
+ klass_name = method ? name : klass
- completions << klass if classes.key? klass # to complete a method name
- elsif selector
- completions << klass if classes.key? klass
- elsif classes.key? klass_name
- completions << klass_name
- end
- end
+ if name !~ /#|\./
+ completions.replace klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/)
+ completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if
+ name =~ /::$/
- def complete_method(name, klass, selector, completions) # :nodoc:
- if completions.include? klass and name =~ /#|\.|::/
- methods = list_methods_matching name
-
- if not methods.empty?
- # remove Foo if given Foo:: and a method was found
- completions.delete klass
- elsif selector
- # replace Foo with Foo:: as given
- completions.delete klass
- completions << "#{klass}#{selector}"
+ completions << klass if classes.key? klass # to complete a method name
+ elsif selector
+ completions << klass if classes.key? klass
+ elsif classes.key? klass_name
+ completions << klass_name
+ end
end
- methods.each do |klass_sel_method|
- match = klass_sel_method.match(/^(.+)(#|\.|::)([^#.:]+)$/)
- # match[2] is `::` for class method and `#` for instance method.
- # To be consistent with old completion that completes `['Foo#i', 'Foo::c']` for `Foo.`,
- # `.` should be a wildcard for both `#` and `::` here.
- if match && match[2] == selector || selector == '.'
- completions << match[1] + selector + match[3]
+ def complete_method(name, klass, selector, completions) # :nodoc:
+ if completions.include? klass and name =~ /#|\.|::/
+ methods = list_methods_matching name
+
+ if not methods.empty?
+ # remove Foo if given Foo:: and a method was found
+ completions.delete klass
+ elsif selector
+ # replace Foo with Foo:: as given
+ completions.delete klass
+ completions << "#{klass}#{selector}"
+ end
+
+ methods.each do |klass_sel_method|
+ match = klass_sel_method.match(/^(.+)(#|\.|::)([^#.:]+)$/)
+ # match[2] is `::` for class method and `#` for instance method.
+ # To be consistent with old completion that completes `['Foo#i', 'Foo::c']` for `Foo.`,
+ # `.` should be a wildcard for both `#` and `::` here.
+ if match && match[2] == selector || selector == '.'
+ completions << match[1] + selector + match[3]
+ end
+ end
end
end
- end
- end
- ##
- # Converts +document+ to text and writes it to the pager
+ ##
+ # Converts +document+ to text and writes it to the pager
- def display(document)
- page do |io|
- f = formatter(io)
- f.width = @width if @width and f.respond_to?(:width)
- text = document.accept f
+ def display(document)
+ page do |io|
+ f = formatter(io)
+ f.width = @width if @width and f.respond_to?(:width)
+ text = document.accept f
- io.write text
- end
- end
+ io.write text
+ end
+ end
- ##
- # Outputs formatted RI data for class +name+. Groups undocumented classes
+ ##
+ # Outputs formatted RI data for class +name+. Groups undocumented classes
- def display_class(name)
- return if name =~ /#|\./
+ def display_class(name)
+ return if name =~ /#|\./
- found, klasses, includes, extends =
- classes_and_includes_and_extends_for name
+ found, klasses, includes, extends =
+ classes_and_includes_and_extends_for name
- return if found.empty?
+ return if found.empty?
- out = class_document name, found, klasses, includes, extends
+ out = class_document name, found, klasses, includes, extends
- display out
- end
+ display out
+ end
- ##
- # Outputs formatted RI data for method +name+
+ ##
+ # Outputs formatted RI data for method +name+
- def display_method(name)
- out = RDoc::Markup::Document.new
+ def display_method(name)
+ out = Markup::Document.new
- add_method out, name
+ add_method out, name
- expand_rdoc_refs_at_the_bottom(out)
+ expand_rdoc_refs_at_the_bottom(out)
- display out
- end
+ display out
+ end
- ##
- # Outputs formatted RI data for the class or method +name+.
- #
- # Returns true if +name+ was found, false if it was not an alternative could
- # be guessed, raises an error if +name+ couldn't be guessed.
+ ##
+ # Outputs formatted RI data for the class or method +name+.
+ #
+ # Returns true if +name+ was found, false if it was not an alternative could
+ # be guessed, raises an error if +name+ couldn't be guessed.
- def display_name(name)
- if name =~ /\w:(\w|$)/
- display_page name
- return true
- end
+ def display_name(name)
+ if name =~ /\w:(\w|$)/
+ display_page name
+ return true
+ end
- return true if display_class name
+ return true if display_class name
- display_method name if name =~ /::|#|\./
+ display_method name if name =~ /::|#|\./
- true
- rescue NotFoundError
- matches = list_methods_matching name if name =~ /::|#|\./
- matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty?
+ true
+ rescue NotFoundError
+ matches = list_methods_matching name if name =~ /::|#|\./
+ matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty?
- raise if matches.empty?
+ raise if matches.empty?
- page do |io|
- io.puts "#{name} not found, maybe you meant:"
- io.puts
- io.puts matches.sort.join("\n")
- end
+ page do |io|
+ io.puts "#{name} not found, maybe you meant:"
+ io.puts
+ io.puts matches.sort.join("\n")
+ end
- false
- end
+ false
+ end
- ##
- # Displays each name in +name+
+ ##
+ # Displays each name in +name+
- def display_names(names)
- names.each do |name|
- name = expand_name name
+ def display_names(names)
+ names.each do |name|
+ name = expand_name name
- display_name name
- end
- end
+ display_name name
+ end
+ end
- ##
- # Outputs formatted RI data for page +name+.
+ ##
+ # Outputs formatted RI data for page +name+.
- def display_page(name)
- store_name, page_name = name.split ':', 2
+ def display_page(name)
+ store_name, page_name = name.split ':', 2
- store = @stores.find { |s| s.source == store_name }
+ store = @stores.find { |s| s.source == store_name }
- return display_page_list store if page_name.empty?
+ return display_page_list store if page_name.empty?
- pages = store.cache[:pages]
+ pages = store.cache[:pages]
- unless pages.include? page_name
- found_names = pages.select do |n|
- n =~ /#{Regexp.escape page_name}\.[^.]+$/
- end
+ unless pages.include? page_name
+ found_names = pages.select do |n|
+ n =~ /#{Regexp.escape page_name}\.[^.]+$/
+ end
- if found_names.length.zero?
- return display_page_list store, pages
- elsif found_names.length > 1
- return display_page_list store, found_names, page_name
- end
+ if found_names.length.zero?
+ return display_page_list store, pages
+ elsif found_names.length > 1
+ return display_page_list store, found_names, page_name
+ end
- page_name = found_names.first
- end
+ page_name = found_names.first
+ end
- page = store.load_page page_name
+ page = store.load_page page_name
- display page.comment.parse
- end
+ display page.comment.parse
+ end
- ##
- # Outputs a formatted RI page list for the pages in +store+.
+ ##
+ # Outputs a formatted RI page list for the pages in +store+.
- def display_page_list(store, pages = store.cache[:pages], search = nil)
- out = RDoc::Markup::Document.new
+ def display_page_list(store, pages = store.cache[:pages], search = nil)
+ out = Markup::Document.new
- title = if search
- "#{search} pages"
- else
- 'Pages'
- end
+ title = if search
+ "#{search} pages"
+ else
+ 'Pages'
+ end
- out << RDoc::Markup::Heading.new(1, "#{title} in #{store.friendly_path}")
- out << RDoc::Markup::BlankLine.new
+ out << Markup::Heading.new(1, "#{title} in #{store.friendly_path}")
+ out << Markup::BlankLine.new
- list = RDoc::Markup::List.new(:BULLET)
+ list = Markup::List.new(:BULLET)
- pages.each do |page|
- list << RDoc::Markup::Paragraph.new(page)
- end
+ pages.each do |page|
+ list << Markup::Paragraph.new(page)
+ end
- out << list
+ out << list
- display out
- end
+ display out
+ end
- def check_did_you_mean # :nodoc:
- if defined? DidYouMean::SpellChecker
- true
- else
- begin
- require 'did_you_mean'
+ def check_did_you_mean # :nodoc:
if defined? DidYouMean::SpellChecker
true
else
- false
+ begin
+ require 'did_you_mean'
+ if defined? DidYouMean::SpellChecker
+ true
+ else
+ false
+ end
+ rescue LoadError
+ false
+ end
end
- rescue LoadError
- false
end
- end
- end
- ##
- # Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
- # will be expanded to Zlib::DataError.
-
- def expand_class(klass)
- class_names = classes.keys
- ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
- if ary.length != 1 && ary.first != klass
- if check_did_you_mean
- suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass) }
- raise NotFoundError.new(klass, suggestion_proc)
- else
- raise NotFoundError, klass
+ ##
+ # Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
+ # will be expanded to Zlib::DataError.
+
+ def expand_class(klass)
+ class_names = classes.keys
+ ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
+ if ary.length != 1 && ary.first != klass
+ if check_did_you_mean
+ suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass) }
+ raise NotFoundError.new(klass, suggestion_proc)
+ else
+ raise NotFoundError, klass
+ end
+ end
+ ary.first
end
- end
- ary.first
- end
- ##
- # Expands the class portion of +name+ into a fully-qualified class. See
- # #expand_class.
+ ##
+ # Expands the class portion of +name+ into a fully-qualified class. See
+ # #expand_class.
- def expand_name(name)
- klass, selector, method = parse_name name
+ def expand_name(name)
+ klass, selector, method = parse_name name
- return [selector, method].join if klass.empty?
+ return [selector, method].join if klass.empty?
- case selector
- when ':'
- [find_store(klass), selector, method]
- else
- [expand_class(klass), selector, method]
- end.join
- end
+ case selector
+ when ':'
+ [find_store(klass), selector, method]
+ else
+ [expand_class(klass), selector, method]
+ end.join
+ end
- ##
- # Filters the methods in +found+ trying to find a match for +name+.
+ ##
+ # Filters the methods in +found+ trying to find a match for +name+.
- def filter_methods(found, name)
- regexp = name_regexp name
+ def filter_methods(found, name)
+ regexp = name_regexp name
- filtered = found.find_all do |store, methods|
- methods.any? { |method| method.full_name =~ regexp }
- end
+ filtered = found.find_all do |store, methods|
+ methods.any? { |method| method.full_name =~ regexp }
+ end
- return filtered unless filtered.empty?
+ return filtered unless filtered.empty?
- found
- end
+ found
+ end
- ##
- # Yields items matching +name+ including the store they were found in, the
- # class being searched for, the class they were found in (an ancestor) the
- # types of methods to look up (from #method_type), and the method name being
- # searched for
+ ##
+ # Yields items matching +name+ including the store they were found in, the
+ # class being searched for, the class they were found in (an ancestor) the
+ # types of methods to look up (from #method_type), and the method name being
+ # searched for
- def find_methods(name)
- klass, selector, method = parse_name name
+ def find_methods(name)
+ klass, selector, method = parse_name name
- types = method_type selector
+ types = method_type selector
- klasses = nil
- ambiguous = klass.empty?
+ klasses = nil
+ ambiguous = klass.empty?
- if ambiguous
- klasses = classes.keys
- else
- klasses = ancestors_of klass
- klasses.unshift klass
- end
+ if ambiguous
+ klasses = classes.keys
+ else
+ klasses = ancestors_of klass
+ klasses.unshift klass
+ end
- methods = []
+ methods = []
- klasses.each do |ancestor|
- ancestors = classes[ancestor]
+ klasses.each do |ancestor|
+ ancestors = classes[ancestor]
- next unless ancestors
+ next unless ancestors
- klass = ancestor if ambiguous
+ klass = ancestor if ambiguous
- ancestors.each do |store|
- methods << [store, klass, ancestor, types, method]
- end
- end
+ ancestors.each do |store|
+ methods << [store, klass, ancestor, types, method]
+ end
+ end
- methods = methods.sort_by do |_, k, a, _, m|
- [k, a, m].compact
- end
+ methods = methods.sort_by do |_, k, a, _, m|
+ [k, a, m].compact
+ end
- methods.each do |item|
- yield(*item) # :yields: store, klass, ancestor, types, method
- end
+ methods.each do |item|
+ yield(*item) # :yields: store, klass, ancestor, types, method
+ end
- self
- end
+ self
+ end
- ##
- # Finds a store that matches +name+ which can be the name of a gem, "ruby",
- # "home" or "site".
- #
- # See also RDoc::Store#source
+ ##
+ # Finds a store that matches +name+ which can be the name of a gem, "ruby",
+ # "home" or "site".
+ #
+ # See also RDoc::Store#source
- def find_store(name)
- @stores.each do |store|
- source = store.source
+ def find_store(name)
+ @stores.each do |store|
+ source = store.source
- return source if source == name
+ return source if source == name
- return source if
- store.type == :gem and source =~ /^#{Regexp.escape name}-\d/
- end
+ return source if
+ store.type == :gem and source =~ /^#{Regexp.escape name}-\d/
+ end
- raise RDoc::RI::Driver::NotFoundError, name
- end
+ raise RI::Driver::NotFoundError, name
+ end
- ##
- # Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
- # use it. If we're outputting to a pager, use bs, otherwise ansi.
-
- def formatter(io)
- if @formatter_klass
- @formatter_klass.new
- elsif paging? or !io.tty?
- RDoc::Markup::ToBs.new
- else
- RDoc::Markup::ToAnsi.new
- end
- end
+ ##
+ # Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
+ # use it. If we're outputting to a pager, use bs, otherwise ansi.
- ##
- # Runs ri interactively using Readline if it is available.
+ def formatter(io)
+ if @formatter_klass
+ @formatter_klass.new
+ elsif paging? or !io.tty?
+ Markup::ToBs.new
+ else
+ Markup::ToAnsi.new
+ end
+ end
- def interactive
- puts "\nEnter the method name you want to look up."
+ ##
+ # Runs ri interactively using Readline if it is available.
- begin
- require 'readline'
- rescue LoadError
- end
- if defined? Readline
- Readline.completion_proc = method :complete
- puts "You can use tab to autocomplete."
- end
+ def interactive
+ puts "\nEnter the method name you want to look up."
+
+ begin
+ require 'readline'
+ rescue LoadError
+ end
+ if defined? Readline
+ Readline.completion_proc = method :complete
+ puts "You can use tab to autocomplete."
+ end
- puts "Enter a blank line to exit.\n\n"
+ puts "Enter a blank line to exit.\n\n"
- loop do
- name = if defined? Readline
- Readline.readline ">> ", true
- else
- print ">> "
- $stdin.gets
- end
+ loop do
+ name = if defined? Readline
+ Readline.readline ">> ", true
+ else
+ print ">> "
+ $stdin.gets
+ end
- return if name.nil? or name.empty?
+ return if name.nil? or name.empty?
- begin
- display_name expand_name(name.strip)
- rescue NotFoundError => e
- puts e.message
+ begin
+ display_name expand_name(name.strip)
+ rescue NotFoundError => e
+ puts e.message
+ end
+ end
+
+ rescue Interrupt
+ exit
end
- end
- rescue Interrupt
- exit
- end
+ ##
+ # Lists classes known to ri starting with +names+. If +names+ is empty all
+ # known classes are shown.
- ##
- # Lists classes known to ri starting with +names+. If +names+ is empty all
- # known classes are shown.
+ def list_known_classes(names = [])
+ classes = []
- def list_known_classes(names = [])
- classes = []
+ stores.each do |store|
+ classes << store.module_names
+ end
- stores.each do |store|
- classes << store.module_names
- end
+ classes = classes.flatten.uniq.sort
- classes = classes.flatten.uniq.sort
+ unless names.empty?
+ filter = Regexp.union names.map { |name| /^#{name}/ }
- unless names.empty?
- filter = Regexp.union names.map { |name| /^#{name}/ }
+ classes = classes.grep filter
+ end
- classes = classes.grep filter
- end
+ page do |io|
+ if paging? or io.tty?
+ if names.empty?
+ io.puts "Classes and Modules known to ri:"
+ else
+ io.puts "Classes and Modules starting with #{names.join ', '}:"
+ end
+ io.puts
+ end
- page do |io|
- if paging? or io.tty?
- if names.empty?
- io.puts "Classes and Modules known to ri:"
- else
- io.puts "Classes and Modules starting with #{names.join ', '}:"
+ io.puts classes.join("\n")
end
- io.puts
end
- io.puts classes.join("\n")
- end
- end
+ ##
+ # Returns an Array of methods matching +name+
- ##
- # Returns an Array of methods matching +name+
+ def list_methods_matching(name)
+ found = []
- def list_methods_matching(name)
- found = []
+ find_methods name do |store, klass, ancestor, types, method|
+ if types == :instance or types == :both
+ methods = store.instance_methods[ancestor]
- find_methods name do |store, klass, ancestor, types, method|
- if types == :instance or types == :both
- methods = store.instance_methods[ancestor]
+ if methods
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
- if methods
- matches = methods.grep(/^#{Regexp.escape method.to_s}/)
+ matches = matches.map do |match|
+ "#{klass}##{match}"
+ end
- matches = matches.map do |match|
- "#{klass}##{match}"
+ found.concat matches
+ end
end
- found.concat matches
- end
- end
+ if types == :class or types == :both
+ methods = store.class_methods[ancestor]
- if types == :class or types == :both
- methods = store.class_methods[ancestor]
+ next unless methods
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
- next unless methods
- matches = methods.grep(/^#{Regexp.escape method.to_s}/)
+ matches = matches.map do |match|
+ "#{klass}::#{match}"
+ end
- matches = matches.map do |match|
- "#{klass}::#{match}"
+ found.concat matches
+ end
end
- found.concat matches
+ found.uniq
end
- end
-
- found.uniq
- end
- ##
- # Loads RI data for method +name+ on +klass+ from +store+. +type+ and
- # +cache+ indicate if it is a class or instance method.
+ ##
+ # Loads RI data for method +name+ on +klass+ from +store+. +type+ and
+ # +cache+ indicate if it is a class or instance method.
- def load_method(store, cache, klass, type, name)
- methods = store.public_send(cache)[klass]
+ def load_method(store, cache, klass, type, name)
+ methods = store.public_send(cache)[klass]
- return unless methods
+ return unless methods
- method = methods.find do |method_name|
- method_name == name
- end
+ method = methods.find do |method_name|
+ method_name == name
+ end
- return unless method
+ return unless method
- store.load_method klass, "#{type}#{method}"
- rescue RDoc::Store::MissingFileError => e
- comment = RDoc::Comment.new("missing documentation at #{e.file}")
- comment.parse
+ store.load_method klass, "#{type}#{method}"
+ rescue ::RDoc::Store::MissingFileError => e
+ comment = Comment.new("missing documentation at #{e.file}")
+ comment.parse
- method = RDoc::AnyMethod.new name
- method.comment = comment
- method
- end
+ method = AnyMethod.new name
+ method.comment = comment
+ method
+ end
- ##
- # Returns an Array of RI data for methods matching +name+
+ ##
+ # Returns an Array of RI data for methods matching +name+
- def load_methods_matching(name)
- found = []
+ def load_methods_matching(name)
+ found = []
- find_methods name do |store, klass, ancestor, types, method|
- methods = []
+ find_methods name do |store, klass, ancestor, types, method|
+ methods = []
- methods << load_method(store, :class_methods, ancestor, '::', method) if
- [:class, :both].include? types
+ methods << load_method(store, :class_methods, ancestor, '::', method) if
+ [:class, :both].include? types
- methods << load_method(store, :instance_methods, ancestor, '#', method) if
- [:instance, :both].include? types
+ methods << load_method(store, :instance_methods, ancestor, '#', method) if
+ [:instance, :both].include? types
- found << [store, methods.compact]
- end
+ found << [store, methods.compact]
+ end
- found.reject do |path, methods| methods.empty? end
- end
+ found.reject do |path, methods| methods.empty? end
+ end
- ##
- # Returns a filtered list of methods matching +name+
+ ##
+ # Returns a filtered list of methods matching +name+
- def lookup_method(name)
- found = load_methods_matching name
+ def lookup_method(name)
+ found = load_methods_matching name
- if found.empty?
- if check_did_you_mean
- methods = []
- _, _, method_name = parse_name name
- find_methods name do |store, klass, ancestor, types, method|
- methods.push(*store.class_methods[klass]) if [:class, :both].include? types
- methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
+ if found.empty?
+ if check_did_you_mean
+ methods = []
+ _, _, method_name = parse_name name
+ find_methods name do |store, klass, ancestor, types, method|
+ methods.push(*store.class_methods[klass]) if [:class, :both].include? types
+ methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
+ end
+ methods = methods.uniq
+ suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name) }
+ raise NotFoundError.new(name, suggestion_proc)
+ else
+ raise NotFoundError, name
+ end
end
- methods = methods.uniq
- suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name) }
- raise NotFoundError.new(name, suggestion_proc)
- else
- raise NotFoundError, name
+
+ filter_methods found, name
end
- end
- filter_methods found, name
- end
+ ##
+ # Builds a RDoc::Markup::Document from +found+, +klasses+ and +includes+
- ##
- # Builds a RDoc::Markup::Document from +found+, +klasses+ and +includes+
+ def method_document(out, name, filtered)
+ out << Markup::Heading.new(1, name)
+ out << Markup::BlankLine.new
- def method_document(out, name, filtered)
- out << RDoc::Markup::Heading.new(1, name)
- out << RDoc::Markup::BlankLine.new
+ filtered.each do |store, methods|
+ methods.each do |method|
+ render_method out, store, method, name
+ end
+ end
- filtered.each do |store, methods|
- methods.each do |method|
- render_method out, store, method, name
+ out
end
- end
- out
- end
-
- ##
- # Returns the type of method (:both, :instance, :class) for +selector+
+ ##
+ # Returns the type of method (:both, :instance, :class) for +selector+
- def method_type(selector)
- case selector
- when '.', nil then :both
- when '#' then :instance
- else :class
- end
- end
+ def method_type(selector)
+ case selector
+ when '.', nil then :both
+ when '#' then :instance
+ else :class
+ end
+ end
- ##
- # Returns a regular expression for +name+ that will match an
- # RDoc::AnyMethod's name.
+ ##
+ # Returns a regular expression for +name+ that will match an
+ # RDoc::AnyMethod's name.
- def name_regexp(name)
- klass, type, name = parse_name name
+ def name_regexp(name)
+ klass, type, name = parse_name name
- case type
- when '#', '::'
- /^#{klass}#{type}#{Regexp.escape name}$/
- else
- /^#{klass}(#|::)#{Regexp.escape name}$/
- end
- end
+ case type
+ when '#', '::'
+ /^#{klass}#{type}#{Regexp.escape name}$/
+ else
+ /^#{klass}(#|::)#{Regexp.escape name}$/
+ end
+ end
- ##
- # Paginates output through a pager program.
+ ##
+ # Paginates output through a pager program.
- def page
- if pager = setup_pager
- begin
- yield pager
+ def page
+ if pager = setup_pager
+ begin
+ yield pager
+ ensure
+ pager.close
+ end
+ else
+ yield $stdout
+ end
+ rescue Errno::EPIPE
ensure
- pager.close
+ @paging = false
end
- else
- yield $stdout
- end
- rescue Errno::EPIPE
- ensure
- @paging = false
- end
- ##
- # Are we using a pager?
+ ##
+ # Are we using a pager?
- def paging?
- @paging
- end
+ def paging?
+ @paging
+ end
+
+ ##
+ # Extracts the class, selector and method name parts from +name+ like
+ # Foo::Bar#baz.
+ #
+ # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
+ # method
+
+ def parse_name(name)
+ parts = name.split(/(::?|#|\.)/)
+
+ if parts.length == 1
+ if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/
+ type = '.'
+ meth = parts.pop
+ else
+ type = nil
+ meth = nil
+ end
+ elsif parts.length == 2 or parts.last =~ /::|#|\./
+ type = parts.pop
+ meth = nil
+ elsif parts[1] == ':'
+ klass = parts.shift
+ type = parts.shift
+ meth = parts.join
+ elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/
+ meth = parts.pop
+ type = parts.pop
+ end
+
+ klass ||= parts.join
- ##
- # Extracts the class, selector and method name parts from +name+ like
- # Foo::Bar#baz.
- #
- # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
- # method
-
- def parse_name(name)
- parts = name.split(/(::?|#|\.)/)
-
- if parts.length == 1
- if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/
- type = '.'
- meth = parts.pop
- else
- type = nil
- meth = nil
+ [klass, type, meth]
end
- elsif parts.length == 2 or parts.last =~ /::|#|\./
- type = parts.pop
- meth = nil
- elsif parts[1] == ':'
- klass = parts.shift
- type = parts.shift
- meth = parts.join
- elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/
- meth = parts.pop
- type = parts.pop
- end
- klass ||= parts.join
+ ##
+ # Renders the +klass+ from +store+ to +out+. If the klass has no
+ # documentable items the class is added to +also_in+ instead.
+
+ def render_class(out, store, klass, also_in) # :nodoc:
+ document = klass.comment.parse
+ # TODO the store's cache should always return an empty Array
+ class_methods = store.class_methods[klass.full_name] || []
+ instance_methods = store.instance_methods[klass.full_name] || []
+ attributes = store.attributes[klass.full_name] || []
+
+ if document.empty? and
+ instance_methods.empty? and class_methods.empty?
+ also_in << store
+ return
+ end
- [klass, type, meth]
- end
+ add_from out, store
- ##
- # Renders the +klass+ from +store+ to +out+. If the klass has no
- # documentable items the class is added to +also_in+ instead.
-
- def render_class(out, store, klass, also_in) # :nodoc:
- document = klass.comment.parse
- # TODO the store's cache should always return an empty Array
- class_methods = store.class_methods[klass.full_name] || []
- instance_methods = store.instance_methods[klass.full_name] || []
- attributes = store.attributes[klass.full_name] || []
-
- if document.empty? and
- instance_methods.empty? and class_methods.empty?
- also_in << store
- return
- end
+ class_document_comment out, document
- add_from out, store
+ if class_methods or instance_methods or not klass.constants.empty?
+ out << Markup::Rule.new(1)
+ end
- class_document_comment out, document
+ class_document_constants out, klass
- if class_methods or instance_methods or not klass.constants.empty?
- out << RDoc::Markup::Rule.new(1)
- end
+ add_method_list out, class_methods, 'Class methods'
+ add_method_list out, instance_methods, 'Instance methods'
+ add_method_list out, attributes, 'Attributes'
- class_document_constants out, klass
+ add_method_documentation out, klass if @show_all
+ end
- add_method_list out, class_methods, 'Class methods'
- add_method_list out, instance_methods, 'Instance methods'
- add_method_list out, attributes, 'Attributes'
+ def render_method(out, store, method, name) # :nodoc:
+ out << Markup::Paragraph.new("(from #{store.friendly_path})")
- add_method_documentation out, klass if @show_all
- end
+ unless name =~ /^#{Regexp.escape method.parent_name}/
+ out << Markup::Heading.new(3, "Implementation from #{method.parent_name}")
+ end
- def render_method(out, store, method, name) # :nodoc:
- out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
+ out << Markup::Rule.new(1)
- unless name =~ /^#{Regexp.escape method.parent_name}/
- out << RDoc::Markup::Heading.new(3, "Implementation from #{method.parent_name}")
- end
+ render_method_arguments out, method.arglists
+ sig = method.type_signature_lines || store.rbs_signature_for(method)
+ render_method_type_signature out, sig if sig
+ render_method_superclass out, method
+ if method.is_alias_for
+ al = method.is_alias_for
+ alias_for = store.load_method al.parent_name, "#{al.name_prefix}#{al.name}"
+ render_method_comment out, method, alias_for
+ else
+ render_method_comment out, method
+ end
+ end
- out << RDoc::Markup::Rule.new(1)
-
- render_method_arguments out, method.arglists
- sig = method.type_signature_lines || store.rbs_signature_for(method)
- render_method_type_signature out, sig if sig
- render_method_superclass out, method
- if method.is_alias_for
- al = method.is_alias_for
- alias_for = store.load_method al.parent_name, "#{al.name_prefix}#{al.name}"
- render_method_comment out, method, alias_for
- else
- render_method_comment out, method
- end
- end
+ def render_method_arguments(out, arglists) # :nodoc:
+ return unless arglists
- def render_method_arguments(out, arglists) # :nodoc:
- return unless arglists
+ arglists = arglists.chomp.split "\n"
+ arglists = arglists.map { |line| line + "\n" }
+ out << Markup::Verbatim.new(*arglists)
+ out << Markup::Rule.new(1)
+ end
- arglists = arglists.chomp.split "\n"
- arglists = arglists.map { |line| line + "\n" }
- out << RDoc::Markup::Verbatim.new(*arglists)
- out << RDoc::Markup::Rule.new(1)
- end
+ def render_method_comment(out, method, alias_for = nil)# :nodoc:
+ if alias_for
+ unless method.comment.nil? or method.comment.empty?
+ out << Markup::BlankLine.new
+ out << method.comment.parse
+ end
+ out << Markup::BlankLine.new
+ out << Markup::Paragraph.new("(This method is an alias for #{alias_for.full_name}.)")
+ out << Markup::BlankLine.new
+ out << alias_for.comment.parse
+ out << Markup::BlankLine.new
+ else
+ out << Markup::BlankLine.new
+ out << method.comment.parse
+ out << Markup::BlankLine.new
+ end
+ end
- def render_method_comment(out, method, alias_for = nil)# :nodoc:
- if alias_for
- unless method.comment.nil? or method.comment.empty?
- out << RDoc::Markup::BlankLine.new
- out << method.comment.parse
+ def render_method_type_signature(out, lines) # :nodoc:
+ out << Markup::Verbatim.new(*lines.map { |s| s + "\n" })
end
- out << RDoc::Markup::BlankLine.new
- out << RDoc::Markup::Paragraph.new("(This method is an alias for #{alias_for.full_name}.)")
- out << RDoc::Markup::BlankLine.new
- out << alias_for.comment.parse
- out << RDoc::Markup::BlankLine.new
- else
- out << RDoc::Markup::BlankLine.new
- out << method.comment.parse
- out << RDoc::Markup::BlankLine.new
- end
- end
- def render_method_type_signature(out, lines) # :nodoc:
- out << RDoc::Markup::Verbatim.new(*lines.map { |s| s + "\n" })
- end
+ def render_method_superclass(out, method) # :nodoc:
+ return unless
+ method.respond_to?(:superclass_method) and method.superclass_method
- def render_method_superclass(out, method) # :nodoc:
- return unless
- method.respond_to?(:superclass_method) and method.superclass_method
+ out << Markup::BlankLine.new
+ out << Markup::Heading.new(4, "(Uses superclass method #{method.superclass_method})")
+ out << Markup::Rule.new(1)
+ end
- out << RDoc::Markup::BlankLine.new
- out << RDoc::Markup::Heading.new(4, "(Uses superclass method #{method.superclass_method})")
- out << RDoc::Markup::Rule.new(1)
- end
+ ##
+ # Looks up and displays ri data according to the options given.
+
+ def run
+ if @list_doc_dirs
+ puts @doc_dirs
+ elsif @list
+ list_known_classes @names
+ elsif @server
+ start_server
+ elsif @interactive or @names.empty?
+ interactive
+ else
+ display_names @names
+ end
+ rescue NotFoundError => e
+ abort e.message
+ end
- ##
- # Looks up and displays ri data according to the options given.
-
- def run
- if @list_doc_dirs
- puts @doc_dirs
- elsif @list
- list_known_classes @names
- elsif @server
- start_server
- elsif @interactive or @names.empty?
- interactive
- else
- display_names @names
- end
- rescue NotFoundError => e
- abort e.message
- end
+ ##
+ # Sets up a pager program to pass output through. Tries the RI_PAGER and
+ # PAGER environment variables followed by pager, less then more.
- ##
- # Sets up a pager program to pass output through. Tries the RI_PAGER and
- # PAGER environment variables followed by pager, less then more.
+ def setup_pager
+ return if @use_stdout
- def setup_pager
- return if @use_stdout
+ pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
- pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
+ require 'shellwords'
+ pagers.compact.uniq.each do |pager|
+ pager = Shellwords.split(pager)
+ next if pager.empty?
- require 'shellwords'
- pagers.compact.uniq.each do |pager|
- pager = Shellwords.split(pager)
- next if pager.empty?
+ io = IO.popen(pager, 'w') rescue next
+ next if $? and $?.pid == io.pid and $?.exited? # pager didn't work
- io = IO.popen(pager, 'w') rescue next
- next if $? and $?.pid == io.pid and $?.exited? # pager didn't work
+ @paging = true
- @paging = true
+ return io
+ end
- return io
- end
+ @use_stdout = true
- @use_stdout = true
+ nil
+ end
- nil
- end
+ ##
+ # Starts a WEBrick server for ri.
- ##
- # Starts a WEBrick server for ri.
+ def start_server
+ begin
+ require 'webrick'
+ rescue LoadError
+ abort "webrick is not found. You may need to `gem install webrick` to install webrick."
+ end
- def start_server
- begin
- require 'webrick'
- rescue LoadError
- abort "webrick is not found. You may need to `gem install webrick` to install webrick."
- end
+ server = WEBrick::HTTPServer.new :Port => @server
- server = WEBrick::HTTPServer.new :Port => @server
+ extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact
- extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact
+ server.mount '/', RI::Servlet, nil, extra_doc_dirs
- server.mount '/', RDoc::RI::Servlet, nil, extra_doc_dirs
+ trap 'INT' do server.shutdown end
+ trap 'TERM' do server.shutdown end
- trap 'INT' do server.shutdown end
- trap 'TERM' do server.shutdown end
+ server.start
+ end
- server.start
- end
+ RDOC_REFS_REGEXP = /\[rdoc-ref:([\w.]+)(@.*)?\]/
- RDOC_REFS_REGEXP = /\[rdoc-ref:([\w.]+)(@.*)?\]/
+ def expand_rdoc_refs_at_the_bottom(out)
+ return unless @expand_refs
- def expand_rdoc_refs_at_the_bottom(out)
- return unless @expand_refs
+ extracted_rdoc_refs = []
- extracted_rdoc_refs = []
+ out.each do |part|
+ content = if part.respond_to?(:text)
+ part.text
+ else
+ next
+ end
- out.each do |part|
- content = if part.respond_to?(:text)
- part.text
- else
- next
- end
+ rdoc_refs = content.scan(RDOC_REFS_REGEXP).uniq.map do |file_name, _anchor|
+ file_name
+ end
- rdoc_refs = content.scan(RDOC_REFS_REGEXP).uniq.map do |file_name, _anchor|
- file_name
- end
+ extracted_rdoc_refs.concat(rdoc_refs)
+ end
- extracted_rdoc_refs.concat(rdoc_refs)
- end
+ found_pages = extracted_rdoc_refs.map do |ref|
+ begin
+ @stores.first.load_page(ref)
+ rescue ::RDoc::Store::MissingFileError
+ end
+ end.compact
- found_pages = extracted_rdoc_refs.map do |ref|
- begin
- @stores.first.load_page(ref)
- rescue RDoc::Store::MissingFileError
+ found_pages.each do |page|
+ out << Markup::Heading.new(4, "Expanded from #{page.full_name}")
+ out << Markup::BlankLine.new
+ out << page.comment.parse
+ end
end
- end.compact
-
- found_pages.each do |page|
- out << RDoc::Markup::Heading.new(4, "Expanded from #{page.full_name}")
- out << RDoc::Markup::BlankLine.new
- out << page.comment.parse
end
end
end
diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb
index 832a101e6c..ed2606e515 100644
--- a/lib/rdoc/ri/formatter.rb
+++ b/lib/rdoc/ri/formatter.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
-##
-# For RubyGems backwards compatibility
+module RDoc
+ module RI
+ ##
+ # For RubyGems backwards compatibility
-module RDoc::RI::Formatter # :nodoc:
+ module Formatter # :nodoc:
+ end
+ end
end
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
index 067b493132..2e42bb9a95 100644
--- a/lib/rdoc/ri/paths.rb
+++ b/lib/rdoc/ri/paths.rb
@@ -1,171 +1,175 @@
# frozen_string_literal: true
require_relative '../rdoc'
-##
-# The directories where ri data lives. Paths can be enumerated via ::each, or
-# queried individually via ::system_dir, ::site_dir, ::home_dir and ::gem_dir.
-
-module RDoc::RI::Paths
-
- #:stopdoc:
- require 'rbconfig'
-
- version = RbConfig::CONFIG['ruby_version']
+module RDoc
+ module RI
+ ##
+ # The directories where ri data lives. Paths can be enumerated via ::each, or
+ # queried individually via ::system_dir, ::site_dir, ::home_dir and ::gem_dir.
+
+ module Paths
+
+ #:stopdoc:
+ require 'rbconfig'
+
+ version = RbConfig::CONFIG['ruby_version']
+
+ BASE = File.join RbConfig::CONFIG['ridir'], version
+
+ HOMEDIR = ::RDoc.home
+ #:startdoc:
+
+ ##
+ # Iterates over each selected path yielding the directory and type.
+ #
+ # Yielded types:
+ # :system:: Where Ruby's ri data is stored. Yielded when +system+ is
+ # true
+ # :site:: Where ri for installed libraries are stored. Yielded when
+ # +site+ is true. Normally no ri data is stored here.
+ # :home:: ~/.rdoc. Yielded when +home+ is true.
+ # :gem:: ri data for an installed gem. Yielded when +gems+ is true.
+ # :extra:: ri data directory from the command line. Yielded for each
+ # entry in +extra_dirs+
+
+ def self.each(system = true, site = true, home = true, gems = :latest, *extra_dirs) # :yields: directory, type
+ return enum_for __method__, system, site, home, gems, *extra_dirs unless
+ block_given?
+
+ extra_dirs.each do |dir|
+ yield dir, :extra
+ end
- BASE = File.join RbConfig::CONFIG['ridir'], version
+ yield system_dir, :system if system
+ yield site_dir, :site if site
+ yield home_dir, :home if home and HOMEDIR
- HOMEDIR = RDoc.home
- #:startdoc:
+ gemdirs(gems).each do |dir|
+ yield dir, :gem
+ end if gems
- ##
- # Iterates over each selected path yielding the directory and type.
- #
- # Yielded types:
- # :system:: Where Ruby's ri data is stored. Yielded when +system+ is
- # true
- # :site:: Where ri for installed libraries are stored. Yielded when
- # +site+ is true. Normally no ri data is stored here.
- # :home:: ~/.rdoc. Yielded when +home+ is true.
- # :gem:: ri data for an installed gem. Yielded when +gems+ is true.
- # :extra:: ri data directory from the command line. Yielded for each
- # entry in +extra_dirs+
+ nil
+ end
- def self.each(system = true, site = true, home = true, gems = :latest, *extra_dirs) # :yields: directory, type
- return enum_for __method__, system, site, home, gems, *extra_dirs unless
- block_given?
+ ##
+ # The ri directory for the gem with +gem_name+.
- extra_dirs.each do |dir|
- yield dir, :extra
- end
+ def self.gem_dir(name, version)
+ req = Gem::Requirement.new "= #{version}"
- yield system_dir, :system if system
- yield site_dir, :site if site
- yield home_dir, :home if home and HOMEDIR
+ spec = Gem::Specification.find_by_name name, req
- gemdirs(gems).each do |dir|
- yield dir, :gem
- end if gems
+ File.join spec.doc_dir, 'ri'
+ end
- nil
- end
+ ##
+ # The latest installed gems' ri directories. +filter+ can be :all or
+ # :latest.
+ #
+ # A +filter+ :all includes all versions of gems and includes gems without
+ # ri documentation.
- ##
- # The ri directory for the gem with +gem_name+.
+ def self.gemdirs(filter = :latest)
+ ri_paths = {}
- def self.gem_dir(name, version)
- req = Gem::Requirement.new "= #{version}"
+ all = Gem::Specification.map do |spec|
+ [File.join(spec.doc_dir, 'ri'), spec.name, spec.version]
+ end
- spec = Gem::Specification.find_by_name name, req
+ if filter == :all
+ gemdirs = []
+
+ all.group_by do |_, name, _|
+ name
+ end.sort_by do |group, _|
+ group
+ end.map do |group, items|
+ items.sort_by do |_, _, version|
+ version
+ end.reverse_each do |dir,|
+ gemdirs << dir
+ end
+ end
+
+ return gemdirs
+ end
- File.join spec.doc_dir, 'ri'
- end
+ all.each do |dir, name, ver|
+ next unless File.exist? dir
- ##
- # The latest installed gems' ri directories. +filter+ can be :all or
- # :latest.
- #
- # A +filter+ :all includes all versions of gems and includes gems without
- # ri documentation.
+ if ri_paths[name].nil? or ver > ri_paths[name].first
+ ri_paths[name] = [ver, name, dir]
+ end
+ end
- def self.gemdirs(filter = :latest)
- ri_paths = {}
+ ri_paths.sort_by { |_, (_, name, _)| name }.map { |k, v| v.last }
+ rescue LoadError
+ []
+ end
- all = Gem::Specification.map do |spec|
- [File.join(spec.doc_dir, 'ri'), spec.name, spec.version]
- end
+ ##
+ # The location of the rdoc data in the user's home directory.
+ #
+ # Like ::system, ri data in the user's home directory is rare and predates
+ # libraries distributed via RubyGems. ri data is rarely generated into this
+ # directory.
- if filter == :all
- gemdirs = []
-
- all.group_by do |_, name, _|
- name
- end.sort_by do |group, _|
- group
- end.map do |group, items|
- items.sort_by do |_, _, version|
- version
- end.reverse_each do |dir,|
- gemdirs << dir
- end
+ def self.home_dir
+ HOMEDIR
end
- return gemdirs
- end
+ ##
+ # Returns existing directories from the selected documentation directories
+ # as an Array.
+ #
+ # See also ::each
- all.each do |dir, name, ver|
- next unless File.exist? dir
+ def self.path(system = true, site = true, home = true, gems = :latest, *extra_dirs)
+ path = raw_path system, site, home, gems, *extra_dirs
- if ri_paths[name].nil? or ver > ri_paths[name].first
- ri_paths[name] = [ver, name, dir]
+ path.select { |directory| File.directory? directory }
end
- end
-
- ri_paths.sort_by { |_, (_, name, _)| name }.map { |k, v| v.last }
- rescue LoadError
- []
- end
- ##
- # The location of the rdoc data in the user's home directory.
- #
- # Like ::system, ri data in the user's home directory is rare and predates
- # libraries distributed via RubyGems. ri data is rarely generated into this
- # directory.
+ ##
+ # Returns selected documentation directories including nonexistent
+ # directories.
+ #
+ # See also ::each
- def self.home_dir
- HOMEDIR
- end
+ def self.raw_path(system, site, home, gems, *extra_dirs)
+ path = []
- ##
- # Returns existing directories from the selected documentation directories
- # as an Array.
- #
- # See also ::each
+ each(system, site, home, gems, *extra_dirs) do |dir, type|
+ path << dir
+ end
- def self.path(system = true, site = true, home = true, gems = :latest, *extra_dirs)
- path = raw_path system, site, home, gems, *extra_dirs
+ path.compact
+ end
- path.select { |directory| File.directory? directory }
- end
+ ##
+ # The location of ri data installed into the site dir.
+ #
+ # Historically this was available for documentation installed by Ruby
+ # libraries predating RubyGems. It is unlikely to contain any content for
+ # modern Ruby installations.
- ##
- # Returns selected documentation directories including nonexistent
- # directories.
- #
- # See also ::each
+ def self.site_dir
+ File.join BASE, 'site'
+ end
- def self.raw_path(system, site, home, gems, *extra_dirs)
- path = []
+ ##
+ # The location of the built-in ri data.
+ #
+ # This data is built automatically when `make` is run when Ruby is
+ # installed. If you did not install Ruby by hand you may need to install
+ # the documentation yourself. Please consult the documentation for your
+ # package manager or Ruby installer for details. You can also use the
+ # rdoc-data gem to install system ri data for common versions of Ruby.
+
+ def self.system_dir
+ File.join BASE, 'system'
+ end
- each(system, site, home, gems, *extra_dirs) do |dir, type|
- path << dir
end
-
- path.compact
- end
-
- ##
- # The location of ri data installed into the site dir.
- #
- # Historically this was available for documentation installed by Ruby
- # libraries predating RubyGems. It is unlikely to contain any content for
- # modern Ruby installations.
-
- def self.site_dir
- File.join BASE, 'site'
- end
-
- ##
- # The location of the built-in ri data.
- #
- # This data is built automatically when `make` is run when Ruby is
- # installed. If you did not install Ruby by hand you may need to install
- # the documentation yourself. Please consult the documentation for your
- # package manager or Ruby installer for details. You can also use the
- # rdoc-data gem to install system ri data for common versions of Ruby.
-
- def self.system_dir
- File.join BASE, 'system'
end
-
end
diff --git a/lib/rdoc/ri/servlet.rb b/lib/rdoc/ri/servlet.rb
index c27aefd828..e8d6bcf997 100644
--- a/lib/rdoc/ri/servlet.rb
+++ b/lib/rdoc/ri/servlet.rb
@@ -10,202 +10,204 @@
abort "webrick is not found. You may need to `gem install webrick` to install webrick."
end
-##
-# This is a WEBrick servlet that allows you to browse ri documentation.
-#
-# You can show documentation through either `ri --server` or, with RubyGems
-# 2.0 or newer, `gem server`. For ri, the server runs on port 8214 by
-# default. For RubyGems the server runs on port 8808 by default.
-#
-# You can use this servlet in your own project by mounting it on a WEBrick
-# server:
-#
-# require 'webrick'
-#
-# server = WEBrick::HTTPServer.new Port: 8000
-#
-# server.mount '/', RDoc::RI::Servlet
-#
-# If you want to mount the servlet some other place than the root, provide the
-# base path when mounting:
-#
-# server.mount '/rdoc', RDoc::RI::Servlet, '/rdoc'
-
-class RDoc::RI::Servlet < WEBrick::HTTPServlet::AbstractServlet
-
- @server_stores = Hash.new { |hash, server| hash[server] = {} }
- @cache = Hash.new { |hash, store| hash[store] = {} }
-
- ##
- # Maps an asset type to its path on the filesystem
-
- attr_reader :asset_dirs
-
- ##
- # An RDoc::Options instance used for rendering options
-
- attr_reader :options
-
- ##
- # Creates an instance of this servlet that shares cached data between
- # requests.
-
- def self.get_instance(server, *options) # :nodoc:
- stores = @server_stores[server]
-
- new server, stores, @cache, *options
- end
-
- ##
- # Creates a new WEBrick servlet.
- #
- # Use +mount_path+ when mounting the servlet somewhere other than /.
- #
- # Use +extra_doc_dirs+ for additional documentation directories.
- #
- # +server+ is provided automatically by WEBrick when mounting. +stores+ and
- # +cache+ are provided automatically by the servlet.
-
- def initialize(server, stores, cache, mount_path = nil, extra_doc_dirs = [])
- super server
-
- @cache = cache
- @mount_path = mount_path
- @extra_doc_dirs = extra_doc_dirs
- @stores = stores
-
- @options = RDoc::Options.new
- @options.op_dir = '.'
-
- darkfish_dir = nil
-
- # HACK dup
- $LOAD_PATH.each do |path|
- darkfish_dir = File.join path, 'rdoc/generator/template/darkfish/'
- next unless File.directory? darkfish_dir
- @options.template_dir = darkfish_dir
- break
- end
+module RDoc
+ module RI
+ ##
+ # This is a WEBrick servlet that allows you to browse ri documentation.
+ #
+ # You can show documentation through either `ri --server` or, with RubyGems
+ # 2.0 or newer, `gem server`. For ri, the server runs on port 8214 by
+ # default. For RubyGems the server runs on port 8808 by default.
+ #
+ # You can use this servlet in your own project by mounting it on a WEBrick
+ # server:
+ #
+ # require 'webrick'
+ #
+ # server = WEBrick::HTTPServer.new Port: 8000
+ #
+ # server.mount '/', RDoc::RI::Servlet
+ #
+ # If you want to mount the servlet some other place than the root, provide the
+ # base path when mounting:
+ #
+ # server.mount '/rdoc', RDoc::RI::Servlet, '/rdoc'
+
+ class Servlet < WEBrick::HTTPServlet::AbstractServlet
+
+ @server_stores = Hash.new { |hash, server| hash[server] = {} }
+ @cache = Hash.new { |hash, store| hash[store] = {} }
+
+ ##
+ # Maps an asset type to its path on the filesystem
+
+ attr_reader :asset_dirs
+
+ ##
+ # An RDoc::Options instance used for rendering options
+
+ attr_reader :options
+
+ ##
+ # Creates an instance of this servlet that shares cached data between
+ # requests.
+
+ def self.get_instance(server, *options) # :nodoc:
+ stores = @server_stores[server]
+
+ new server, stores, @cache, *options
+ end
- @asset_dirs = {
- :darkfish => darkfish_dir,
- :json_index =>
- File.expand_path('../generator/template/json_index/', __FILE__),
- }
- end
+ ##
+ # Creates a new WEBrick servlet.
+ #
+ # Use +mount_path+ when mounting the servlet somewhere other than /.
+ #
+ # Use +extra_doc_dirs+ for additional documentation directories.
+ #
+ # +server+ is provided automatically by WEBrick when mounting. +stores+ and
+ # +cache+ are provided automatically by the servlet.
+
+ def initialize(server, stores, cache, mount_path = nil, extra_doc_dirs = [])
+ super server
+
+ @cache = cache
+ @mount_path = mount_path
+ @extra_doc_dirs = extra_doc_dirs
+ @stores = stores
+
+ @options = Options.new
+ @options.op_dir = '.'
+
+ darkfish_dir = nil
+
+ # HACK dup
+ $LOAD_PATH.each do |path|
+ darkfish_dir = File.join path, 'rdoc/generator/template/darkfish/'
+ next unless File.directory? darkfish_dir
+ @options.template_dir = darkfish_dir
+ break
+ end
+
+ @asset_dirs = {
+ :darkfish => darkfish_dir,
+ :json_index =>
+ File.expand_path('../generator/template/json_index/', __FILE__),
+ }
+ end
- ##
- # Serves the asset at the path in +req+ for +generator_name+ via +res+.
+ ##
+ # Serves the asset at the path in +req+ for +generator_name+ via +res+.
- def asset(generator_name, req, res)
- asset_dir = @asset_dirs[generator_name]
+ def asset(generator_name, req, res)
+ asset_dir = @asset_dirs[generator_name]
- asset_path = File.join asset_dir, req.path
+ asset_path = File.join asset_dir, req.path
- if_modified_since req, res, asset_path
+ if_modified_since req, res, asset_path
- res.body = File.read asset_path
+ res.body = File.read asset_path
- res.content_type = case req.path
- when /\.css\z/ then 'text/css'
- when /\.js\z/ then 'application/javascript'
- else 'application/octet-stream'
- end
- end
+ res.content_type = case req.path
+ when /\.css\z/ then 'text/css'
+ when /\.js\z/ then 'application/javascript'
+ else 'application/octet-stream'
+ end
+ end
- ##
- # GET request entry point. Fills in +res+ for the path, etc. in +req+.
-
- def do_GET(req, res)
- req.path.sub!(/\A#{Regexp.escape @mount_path}/, '') if @mount_path
-
- case req.path
- when '/'
- root req, res
- when '/js/darkfish.js', '/js/jquery.js', '/js/search.js',
- %r%^/css/%, %r%^/images/%, %r%^/fonts/%
- asset :darkfish, req, res
- when '/js/navigation.js', '/js/searcher.js'
- asset :json_index, req, res
- when '/js/search_index.js'
- root_search req, res
- else
- show_documentation req, res
- end
- rescue WEBrick::HTTPStatus::NotFound => e
- generator = generator_for RDoc::Store.new(@options)
-
- not_found generator, req, res, e.message
- rescue WEBrick::HTTPStatus::Status
- raise
- rescue => e
- error e, req, res
- end
+ ##
+ # GET request entry point. Fills in +res+ for the path, etc. in +req+.
+
+ def do_GET(req, res)
+ req.path.sub!(/\A#{Regexp.escape @mount_path}/, '') if @mount_path
+
+ case req.path
+ when '/'
+ root req, res
+ when '/js/darkfish.js', '/js/jquery.js', '/js/search.js',
+ %r%^/css/%, %r%^/images/%, %r%^/fonts/%
+ asset :darkfish, req, res
+ when '/js/navigation.js', '/js/searcher.js'
+ asset :json_index, req, res
+ when '/js/search_index.js'
+ root_search req, res
+ else
+ show_documentation req, res
+ end
+ rescue WEBrick::HTTPStatus::NotFound => e
+ generator = generator_for ::RDoc::Store.new(@options)
+
+ not_found generator, req, res, e.message
+ rescue WEBrick::HTTPStatus::Status
+ raise
+ rescue => e
+ error e, req, res
+ end
- ##
- # Fills in +res+ with the class, module or page for +req+ from +store+.
- #
- # +path+ is relative to the mount_path and is used to determine the class,
- # module or page name (/RDoc/Servlet.html becomes RDoc::Servlet).
- # +generator+ is used to create the page.
-
- def documentation_page(store, generator, path, req, res)
- text_name = path.chomp '.html'
- name = text_name.gsub '/', '::'
-
- if klass = store.find_class_or_module(name)
- res.body = generator.generate_class klass
- elsif page = store.find_text_page(name.sub(/_([^_]*)\z/, '.\1'))
- res.body = generator.generate_page page
- elsif page = store.find_text_page(text_name.sub(/_([^_]*)\z/, '.\1'))
- res.body = generator.generate_page page
- else
- not_found generator, req, res
- end
- end
+ ##
+ # Fills in +res+ with the class, module or page for +req+ from +store+.
+ #
+ # +path+ is relative to the mount_path and is used to determine the class,
+ # module or page name (/RDoc/Servlet.html becomes RDoc::Servlet).
+ # +generator+ is used to create the page.
+
+ def documentation_page(store, generator, path, req, res)
+ text_name = path.chomp '.html'
+ name = text_name.gsub '/', '::'
+
+ if klass = store.find_class_or_module(name)
+ res.body = generator.generate_class klass
+ elsif page = store.find_text_page(name.sub(/_([^_]*)\z/, '.\1'))
+ res.body = generator.generate_page page
+ elsif page = store.find_text_page(text_name.sub(/_([^_]*)\z/, '.\1'))
+ res.body = generator.generate_page page
+ else
+ not_found generator, req, res
+ end
+ end
- ##
- # Creates the JSON search index on +res+ for the given +store+. +generator+
- # must respond to \#json_index to build. +req+ is ignored.
+ ##
+ # Creates the JSON search index on +res+ for the given +store+. +generator+
+ # must respond to \#json_index to build. +req+ is ignored.
- def documentation_search(store, generator, req, res)
- json_index = @cache[store].fetch :json_index do
- @cache[store][:json_index] =
- JSON.dump generator.json_index.build_index
- end
+ def documentation_search(store, generator, req, res)
+ json_index = @cache[store].fetch :json_index do
+ @cache[store][:json_index] =
+ JSON.dump generator.json_index.build_index
+ end
- res.content_type = 'application/javascript'
- res.body = "var search_data = #{json_index}"
- end
+ res.content_type = 'application/javascript'
+ res.body = "var search_data = #{json_index}"
+ end
- ##
- # Returns the RDoc::Store and path relative to +mount_path+ for
- # documentation at +path+.
+ ##
+ # Returns the RDoc::Store and path relative to +mount_path+ for
+ # documentation at +path+.
- def documentation_source(path)
- _, source_name, path = path.split '/', 3
+ def documentation_source(path)
+ _, source_name, path = path.split '/', 3
- store = @stores[source_name]
- return store, path if store
+ store = @stores[source_name]
+ return store, path if store
- store = store_for source_name
+ store = store_for source_name
- store.load_all
+ store.load_all
- @stores[source_name] = store
+ @stores[source_name] = store
- return store, path
- end
+ return store, path
+ end
- ##
- # Generates an error page for the +exception+ while handling +req+ on +res+.
+ ##
+ # Generates an error page for the +exception+ while handling +req+ on +res+.
- def error(exception, req, res)
- backtrace = exception.backtrace.join "\n"
+ def error(exception, req, res)
+ backtrace = exception.backtrace.join "\n"
- res.content_type = 'text/html'
- res.status = 500
- res.body = <<-BODY
+ res.content_type = 'text/html'
+ res.status = 500
+ res.body = <<-BODY
@@ -219,7 +221,7 @@ def error(exception, req, res)
Error
While processing #{ERB::Util.html_escape req.request_uri} the
-RDoc (#{ERB::Util.html_escape RDoc::VERSION}) server has encountered a
+RDoc (#{ERB::Util.html_escape(VERSION)}) server has encountered a
#{ERB::Util.html_escape exception.class}
exception:
@@ -238,215 +240,217 @@ def error(exception, req, res)
BODY
- end
+ end
- ##
- # Instantiates a Darkfish generator for +store+
+ ##
+ # Instantiates a Darkfish generator for +store+
- def generator_for(store)
- generator = RDoc::Generator::Darkfish.new store, @options
- generator.file_output = false
- generator.asset_rel_path = '..'
- generator.setup
+ def generator_for(store)
+ generator = Generator::Darkfish.new store, @options
+ generator.file_output = false
+ generator.asset_rel_path = '..'
+ generator.setup
- rdoc = RDoc::RDoc.new
- rdoc.store = store
- rdoc.generator = generator
- rdoc.options = @options
+ rdoc = RDoc.new
+ rdoc.store = store
+ rdoc.generator = generator
+ rdoc.options = @options
- @options.main_page = store.main
- @options.title = store.title
+ @options.main_page = store.main
+ @options.title = store.title
- generator
- end
+ generator
+ end
- ##
- # Handles the If-Modified-Since HTTP header on +req+ for +path+. If the
- # file has not been modified a Not Modified response is returned. If the
- # file has been modified a Last-Modified header is added to +res+.
+ ##
+ # Handles the If-Modified-Since HTTP header on +req+ for +path+. If the
+ # file has not been modified a Not Modified response is returned. If the
+ # file has been modified a Last-Modified header is added to +res+.
- def if_modified_since(req, res, path = nil)
- last_modified = File.stat(path).mtime if path
+ def if_modified_since(req, res, path = nil)
+ last_modified = File.stat(path).mtime if path
- res['last-modified'] = last_modified.httpdate
+ res['last-modified'] = last_modified.httpdate
- return unless ims = req['if-modified-since']
+ return unless ims = req['if-modified-since']
- ims = Time.parse ims
+ ims = Time.parse ims
- unless ims < last_modified
- res.body = ''
- raise WEBrick::HTTPStatus::NotModified
- end
- end
+ unless ims < last_modified
+ res.body = ''
+ raise WEBrick::HTTPStatus::NotModified
+ end
+ end
- ##
- # Returns an Array of installed documentation.
- #
- # Each entry contains the documentation name (gem name, 'Ruby
- # Documentation', etc.), the path relative to the mount point, whether the
- # documentation exists, the type of documentation (See RDoc::RI::Paths#each)
- # and the filesystem to the RDoc::Store for the documentation.
-
- def installed_docs
- extra_counter = 0
- ri_paths.map do |path, type|
- store = RDoc::Store.new(@options, path: path, type: type)
- exists = File.exist? store.cache_path
-
- case type
- when :gem
- gem_path = path[%r%/([^/]*)/ri$%, 1]
- [gem_path, "#{gem_path}/", exists, type, path]
- when :system
- ['Ruby Documentation', 'ruby/', exists, type, path]
- when :site
- ['Site Documentation', 'site/', exists, type, path]
- when :home
- ['Home Documentation', 'home/', exists, type, path]
- when :extra
- extra_counter += 1
- store.load_cache if exists
- title = store.title || "Extra Documentation"
- [title, "extra-#{extra_counter}/", exists, type, path]
+ ##
+ # Returns an Array of installed documentation.
+ #
+ # Each entry contains the documentation name (gem name, 'Ruby
+ # Documentation', etc.), the path relative to the mount point, whether the
+ # documentation exists, the type of documentation (See RDoc::RI::Paths#each)
+ # and the filesystem to the RDoc::Store for the documentation.
+
+ def installed_docs
+ extra_counter = 0
+ ri_paths.map do |path, type|
+ store = ::RDoc::Store.new(@options, path: path, type: type)
+ exists = File.exist? store.cache_path
+
+ case type
+ when :gem
+ gem_path = path[%r%/([^/]*)/ri$%, 1]
+ [gem_path, "#{gem_path}/", exists, type, path]
+ when :system
+ ['Ruby Documentation', 'ruby/', exists, type, path]
+ when :site
+ ['Site Documentation', 'site/', exists, type, path]
+ when :home
+ ['Home Documentation', 'home/', exists, type, path]
+ when :extra
+ extra_counter += 1
+ store.load_cache if exists
+ title = store.title || "Extra Documentation"
+ [title, "extra-#{extra_counter}/", exists, type, path]
+ end
+ end
end
- end
- end
- ##
- # Returns a 404 page built by +generator+ for +req+ on +res+.
+ ##
+ # Returns a 404 page built by +generator+ for +req+ on +res+.
- def not_found(generator, req, res, message = nil)
- message ||= "The page #{ERB::Util.h req.path} was not found"
- res.body = generator.generate_servlet_not_found message
- res.status = 404
- end
+ def not_found(generator, req, res, message = nil)
+ message ||= "The page #{ERB::Util.h req.path} was not found"
+ res.body = generator.generate_servlet_not_found message
+ res.status = 404
+ end
- ##
- # Enumerates the ri paths. See RDoc::RI::Paths#each
+ ##
+ # Enumerates the ri paths. See RDoc::RI::Paths#each
- def ri_paths(&block)
- RDoc::RI::Paths.each true, true, true, :all, *@extra_doc_dirs, &block #TODO: pass extra_dirs
- end
+ def ri_paths(&block)
+ RI::Paths.each true, true, true, :all, *@extra_doc_dirs, &block #TODO: pass extra_dirs
+ end
- ##
- # Generates the root page on +res+. +req+ is ignored.
+ ##
+ # Generates the root page on +res+. +req+ is ignored.
- def root(req, res)
- generator = RDoc::Generator::Darkfish.new nil, @options
+ def root(req, res)
+ generator = Generator::Darkfish.new nil, @options
- res.body = generator.generate_servlet_root installed_docs
+ res.body = generator.generate_servlet_root installed_docs
- res.content_type = 'text/html'
- end
+ res.content_type = 'text/html'
+ end
- ##
- # Generates a search index for the root page on +res+. +req+ is ignored.
-
- def root_search(req, res)
- search_index = []
- info = []
-
- installed_docs.map do |name, href, exists, type, path|
- next unless exists
-
- search_index << name
-
- case type
- when :gem
- gemspec = path.gsub(%r%/doc/([^/]*?)/ri$%,
- '/specifications/\1.gemspec')
-
- spec = Gem::Specification.load gemspec
-
- path = spec.full_name
- comment = spec.summary
- when :system
- path = 'ruby'
- comment = 'Documentation for the Ruby standard library'
- when :site
- path = 'site'
- comment = 'Documentation for non-gem libraries'
- when :home
- path = 'home'
- comment = 'Documentation from your home directory'
- when :extra
- comment = name
+ ##
+ # Generates a search index for the root page on +res+. +req+ is ignored.
+
+ def root_search(req, res)
+ search_index = []
+ info = []
+
+ installed_docs.map do |name, href, exists, type, path|
+ next unless exists
+
+ search_index << name
+
+ case type
+ when :gem
+ gemspec = path.gsub(%r%/doc/([^/]*?)/ri$%,
+ '/specifications/\1.gemspec')
+
+ spec = Gem::Specification.load gemspec
+
+ path = spec.full_name
+ comment = spec.summary
+ when :system
+ path = 'ruby'
+ comment = 'Documentation for the Ruby standard library'
+ when :site
+ path = 'site'
+ comment = 'Documentation for non-gem libraries'
+ when :home
+ path = 'home'
+ comment = 'Documentation from your home directory'
+ when :extra
+ comment = name
+ end
+
+ info << [name, '', path, '', comment]
+ end
+
+ index = {
+ :index => {
+ :searchIndex => search_index,
+ :longSearchIndex => search_index,
+ :info => info,
+ }
+ }
+
+ res.body = "var search_data = #{JSON.dump index};"
+ res.content_type = 'application/javascript'
end
- info << [name, '', path, '', comment]
- end
+ ##
+ # Displays documentation for +req+ on +res+, whether that be HTML or some
+ # asset.
+
+ def show_documentation(req, res)
+ store, path = documentation_source req.path
+
+ if_modified_since req, res, store.cache_path
+
+ generator = generator_for store
+
+ case path
+ when nil, '', 'index.html'
+ res.body = generator.generate_index
+ when 'table_of_contents.html'
+ res.body = generator.generate_table_of_contents
+ when 'js/search_index.js'
+ documentation_search store, generator, req, res
+ else
+ documentation_page store, generator, path, req, res
+ end
+ ensure
+ res.content_type ||= 'text/html'
+ end
- index = {
- :index => {
- :searchIndex => search_index,
- :longSearchIndex => search_index,
- :info => info,
- }
- }
+ ##
+ # Returns an RDoc::Store for the given +source_name+ ('ruby' or a gem name).
- res.body = "var search_data = #{JSON.dump index};"
- res.content_type = 'application/javascript'
- end
+ def store_for(source_name)
+ case source_name
+ when 'home'
+ ::RDoc::Store.new(@options, path: RI::Paths.home_dir, type: :home)
+ when 'ruby'
+ ::RDoc::Store.new(@options, path: RI::Paths.system_dir, type: :system)
+ when 'site'
+ ::RDoc::Store.new(@options, path: RI::Paths.site_dir, type: :site)
+ when /\Aextra-(\d+)\z/
+ index = $1.to_i - 1
+ ri_dir = installed_docs[index][4]
+ ::RDoc::Store.new(@options, path: ri_dir, type: :extra)
+ else
+ ri_dir, type = ri_paths.find do |dir, dir_type|
+ next unless dir_type == :gem
- ##
- # Displays documentation for +req+ on +res+, whether that be HTML or some
- # asset.
+ source_name == dir[%r%/([^/]*)/ri$%, 1]
+ end
- def show_documentation(req, res)
- store, path = documentation_source req.path
+ raise WEBrick::HTTPStatus::NotFound,
+ "Could not find gem \"#{ERB::Util.html_escape(source_name)}\". Are you sure you installed it?" unless ri_dir
- if_modified_since req, res, store.cache_path
+ store = ::RDoc::Store.new(@options, path: ri_dir, type: type)
- generator = generator_for store
+ return store if File.exist? store.cache_path
- case path
- when nil, '', 'index.html'
- res.body = generator.generate_index
- when 'table_of_contents.html'
- res.body = generator.generate_table_of_contents
- when 'js/search_index.js'
- documentation_search store, generator, req, res
- else
- documentation_page store, generator, path, req, res
- end
- ensure
- res.content_type ||= 'text/html'
- end
+ raise WEBrick::HTTPStatus::NotFound,
+ "Could not find documentation for \"#{ERB::Util.html_escape(source_name)}\". Please run `gem rdoc --ri gem_name`"
- ##
- # Returns an RDoc::Store for the given +source_name+ ('ruby' or a gem name).
-
- def store_for(source_name)
- case source_name
- when 'home'
- RDoc::Store.new(@options, path: RDoc::RI::Paths.home_dir, type: :home)
- when 'ruby'
- RDoc::Store.new(@options, path: RDoc::RI::Paths.system_dir, type: :system)
- when 'site'
- RDoc::Store.new(@options, path: RDoc::RI::Paths.site_dir, type: :site)
- when /\Aextra-(\d+)\z/
- index = $1.to_i - 1
- ri_dir = installed_docs[index][4]
- RDoc::Store.new(@options, path: ri_dir, type: :extra)
- else
- ri_dir, type = ri_paths.find do |dir, dir_type|
- next unless dir_type == :gem
-
- source_name == dir[%r%/([^/]*)/ri$%, 1]
+ end
end
- raise WEBrick::HTTPStatus::NotFound,
- "Could not find gem \"#{ERB::Util.html_escape(source_name)}\". Are you sure you installed it?" unless ri_dir
-
- store = RDoc::Store.new(@options, path: ri_dir, type: type)
-
- return store if File.exist? store.cache_path
-
- raise WEBrick::HTTPStatus::NotFound,
- "Could not find documentation for \"#{ERB::Util.html_escape(source_name)}\". Please run `gem rdoc --ri gem_name`"
-
end
end
-
end
diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb
index 96742e7ae3..ea2aebfecf 100644
--- a/lib/rdoc/ri/store.rb
+++ b/lib/rdoc/ri/store.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
-module RDoc::RI
+module RDoc
+ module RI
- Store = RDoc::Store # :nodoc:
+ Store = ::RDoc::Store # :nodoc:
+ end
end
diff --git a/lib/rdoc/ri/task.rb b/lib/rdoc/ri/task.rb
index 214ac62208..540952f94d 100644
--- a/lib/rdoc/ri/task.rb
+++ b/lib/rdoc/ri/task.rb
@@ -6,66 +6,70 @@
require_relative '../task'
-##
-# RDoc::RI::Task creates ri data in ./.rdoc for your project.
-#
-# It contains the following tasks:
-#
-# [ri]
-# Build ri data
-#
-# [clobber_ri]
-# Delete ri data files. This target is automatically added to the main
-# clobber target.
-#
-# [reri]
-# Rebuild the ri data from scratch even if they are not out of date.
-#
-# Simple example:
-#
-# require 'rdoc/ri/task'
-#
-# RDoc::RI::Task.new do |ri|
-# ri.main = 'README.md'
-# ri.rdoc_files.include 'README.md', 'lib/**/*.rb'
-# end
-#
-# For further configuration details see RDoc::Task.
+module RDoc
+ module RI
+ ##
+ # RDoc::RI::Task creates ri data in ./.rdoc for your project.
+ #
+ # It contains the following tasks:
+ #
+ # [ri]
+ # Build ri data
+ #
+ # [clobber_ri]
+ # Delete ri data files. This target is automatically added to the main
+ # clobber target.
+ #
+ # [reri]
+ # Rebuild the ri data from scratch even if they are not out of date.
+ #
+ # Simple example:
+ #
+ # require 'rdoc/ri/task'
+ #
+ # RDoc::RI::Task.new do |ri|
+ # ri.main = 'README.md'
+ # ri.rdoc_files.include 'README.md', 'lib/**/*.rb'
+ # end
+ #
+ # For further configuration details see RDoc::Task.
-class RDoc::RI::Task < RDoc::Task
+ class Task < ::RDoc::Task
- DEFAULT_NAMES = { # :nodoc:
- :clobber_rdoc => :clobber_ri,
- :rdoc => :ri,
- :rerdoc => :reri,
- }
+ DEFAULT_NAMES = { # :nodoc:
+ :clobber_rdoc => :clobber_ri,
+ :rdoc => :ri,
+ :rerdoc => :reri,
+ }
- ##
- # Create an ri task with the given name. See RDoc::Task for documentation on
- # setting names.
+ ##
+ # Create an ri task with the given name. See RDoc::Task for documentation on
+ # setting names.
- def initialize(name = DEFAULT_NAMES) # :yield: self
- super
- end
+ def initialize(name = DEFAULT_NAMES) # :yield: self
+ super
+ end
- def clobber_task_description # :nodoc:
- "Remove RI data files"
- end
+ def clobber_task_description # :nodoc:
+ "Remove RI data files"
+ end
- ##
- # Sets default task values
+ ##
+ # Sets default task values
- def defaults
- super
+ def defaults
+ super
- @rdoc_dir = '.rdoc'
- end
+ @rdoc_dir = '.rdoc'
+ end
- def rdoc_task_description # :nodoc:
- 'Build RI data files'
- end
+ def rdoc_task_description # :nodoc:
+ 'Build RI data files'
+ end
- def rerdoc_task_description # :nodoc:
- 'Rebuild RI data files'
+ def rerdoc_task_description # :nodoc:
+ 'Rebuild RI data files'
+ end
+ end
end
end
diff --git a/lib/rdoc/rubygems_hook.rb b/lib/rdoc/rubygems_hook.rb
index e1a1e93494..b209262a4e 100644
--- a/lib/rdoc/rubygems_hook.rb
+++ b/lib/rdoc/rubygems_hook.rb
@@ -3,255 +3,257 @@
require 'fileutils'
require_relative '../rdoc'
-# We define the following two similar name classes in this file:
-#
-# - RDoc::RubyGemsHook
-# - RDoc::RubygemsHook
-#
-# RDoc::RubyGemsHook is the main class that has real logic.
-#
-# RDoc::RubygemsHook is a class that is only for
-# compatibility. RDoc::RubygemsHook is used by RubyGems directly. We
-# can remove this when all maintained RubyGems remove
-# `rubygems/rdoc.rb`.
-
-class RDoc::RubyGemsHook
-
- include Gem::UserInteraction
- extend Gem::UserInteraction
-
- @rdoc_version = nil
- @specs = []
+module RDoc
+ # We define the following two similar name classes in this file:
+ #
+ # - RDoc::RubyGemsHook
+ # - RDoc::RubygemsHook
+ #
+ # RDoc::RubyGemsHook is the main class that has real logic.
+ #
+ # RDoc::RubygemsHook is a class that is only for
+ # compatibility. RDoc::RubygemsHook is used by RubyGems directly. We
+ # can remove this when all maintained RubyGems remove
+ # `rubygems/rdoc.rb`.
- ##
- # Force installation of documentation?
+ class RubyGemsHook
- attr_accessor :force
+ include Gem::UserInteraction
+ extend Gem::UserInteraction
- ##
- # Generate rdoc?
+ @rdoc_version = nil
+ @specs = []
- attr_accessor :generate_rdoc
+ ##
+ # Force installation of documentation?
- ##
- # Generate ri data?
+ attr_accessor :force
- attr_accessor :generate_ri
+ ##
+ # Generate rdoc?
- class << self
+ attr_accessor :generate_rdoc
##
- # Loaded version of RDoc. Set by ::load_rdoc
-
- attr_reader :rdoc_version
+ # Generate ri data?
- end
+ attr_accessor :generate_ri
- ##
- # Post installs hook that generates documentation for each specification in
- # +specs+
+ class << self
- def self.generate(installer, specs)
- start = Time.now
- types = installer.document
+ ##
+ # Loaded version of RDoc. Set by ::load_rdoc
- generate_rdoc = types.include? 'rdoc'
- generate_ri = types.include? 'ri'
+ attr_reader :rdoc_version
- specs.each do |spec|
- new(spec, generate_rdoc, generate_ri).generate
end
- return unless generate_rdoc or generate_ri
+ ##
+ # Post installs hook that generates documentation for each specification in
+ # +specs+
- duration = (Time.now - start).to_i
- names = specs.map(&:name).join ', '
+ def self.generate(installer, specs)
+ start = Time.now
+ types = installer.document
- say "Done installing documentation for #{names} after #{duration} seconds"
- end
+ generate_rdoc = types.include? 'rdoc'
+ generate_ri = types.include? 'ri'
- def self.remove(uninstaller)
- new(uninstaller.spec).remove
- end
+ specs.each do |spec|
+ new(spec, generate_rdoc, generate_ri).generate
+ end
- ##
- # Loads the RDoc generator
+ return unless generate_rdoc or generate_ri
- def self.load_rdoc
- return if @rdoc_version
+ duration = (Time.now - start).to_i
+ names = specs.map(&:name).join ', '
- require_relative 'rdoc'
+ say "Done installing documentation for #{names} after #{duration} seconds"
+ end
- @rdoc_version = Gem::Version.new ::RDoc::VERSION
- end
+ def self.remove(uninstaller)
+ new(uninstaller.spec).remove
+ end
- ##
- # Creates a new documentation generator for +spec+. RDoc and ri data
- # generation can be enabled or disabled through +generate_rdoc+ and
- # +generate_ri+ respectively.
- #
- # Only +generate_ri+ is enabled by default.
+ ##
+ # Loads the RDoc generator
- def initialize(spec, generate_rdoc = false, generate_ri = true)
- @doc_dir = spec.doc_dir
- @force = false
- @rdoc = nil
- @spec = spec
+ def self.load_rdoc
+ return if @rdoc_version
- @generate_rdoc = generate_rdoc
- @generate_ri = generate_ri
+ require_relative 'rdoc'
- @rdoc_dir = spec.doc_dir 'rdoc'
- @ri_dir = spec.doc_dir 'ri'
- end
+ @rdoc_version = Gem::Version.new VERSION
+ end
- ##
- # Removes legacy rdoc arguments from +args+
- #--
- # TODO move to RDoc::Options
+ ##
+ # Creates a new documentation generator for +spec+. RDoc and ri data
+ # generation can be enabled or disabled through +generate_rdoc+ and
+ # +generate_ri+ respectively.
+ #
+ # Only +generate_ri+ is enabled by default.
- def delete_legacy_args(args)
- args.delete '--inline-source'
- args.delete '--promiscuous'
- args.delete '-p'
- args.delete '--one-file'
- end
+ def initialize(spec, generate_rdoc = false, generate_ri = true)
+ @doc_dir = spec.doc_dir
+ @force = false
+ @rdoc = nil
+ @spec = spec
- ##
- # Generates documentation using the named +generator+ ("aliki" or "ri")
- # and following the given +options+.
- #
- # Documentation will be generated into +destination+
+ @generate_rdoc = generate_rdoc
+ @generate_ri = generate_ri
- def document(generator, options, destination)
- generator_name = generator
+ @rdoc_dir = spec.doc_dir 'rdoc'
+ @ri_dir = spec.doc_dir 'ri'
+ end
- options = options.dup
- options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
- options.setup_generator generator
- options.op_dir = destination
- Dir.chdir @spec.full_gem_path do
- options.finish
+ ##
+ # Removes legacy rdoc arguments from +args+
+ #--
+ # TODO move to RDoc::Options
+
+ def delete_legacy_args(args)
+ args.delete '--inline-source'
+ args.delete '--promiscuous'
+ args.delete '-p'
+ args.delete '--one-file'
end
- generator = options.generator.new @rdoc.store, options
+ ##
+ # Generates documentation using the named +generator+ ("aliki" or "ri")
+ # and following the given +options+.
+ #
+ # Documentation will be generated into +destination+
+
+ def document(generator, options, destination)
+ generator_name = generator
+
+ options = options.dup
+ options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
+ options.setup_generator generator
+ options.op_dir = destination
+ Dir.chdir @spec.full_gem_path do
+ options.finish
+ end
+
+ generator = options.generator.new @rdoc.store, options
- @rdoc.options = options
- @rdoc.generator = generator
+ @rdoc.options = options
+ @rdoc.generator = generator
- say "Installing #{generator_name} documentation for #{@spec.full_name}"
+ say "Installing #{generator_name} documentation for #{@spec.full_name}"
- FileUtils.mkdir_p options.op_dir
+ FileUtils.mkdir_p options.op_dir
- Dir.chdir options.op_dir do
- begin
- @rdoc.class.current = @rdoc
- @rdoc.generator.generate
- ensure
- @rdoc.class.current = nil
+ Dir.chdir options.op_dir do
+ begin
+ @rdoc.class.current = @rdoc
+ @rdoc.generator.generate
+ ensure
+ @rdoc.class.current = nil
+ end
end
end
- end
- ##
- # Generates RDoc and ri data
+ ##
+ # Generates RDoc and ri data
- def generate
- return if @spec.default_gem?
- return unless @generate_ri or @generate_rdoc
+ def generate
+ return if @spec.default_gem?
+ return unless @generate_ri or @generate_rdoc
- setup
+ setup
- options = nil
+ options = nil
- args = @spec.rdoc_options
- args.concat @spec.source_paths
- args.concat @spec.extra_rdoc_files
+ args = @spec.rdoc_options
+ args.concat @spec.source_paths
+ args.concat @spec.extra_rdoc_files
- case config_args = Gem.configuration[:rdoc]
- when String
- args = args.concat config_args.split(' ')
- when Array
- args = args.concat config_args
- end
+ case config_args = Gem.configuration[:rdoc]
+ when String
+ args = args.concat config_args.split(' ')
+ when Array
+ args = args.concat config_args
+ end
- delete_legacy_args args
+ delete_legacy_args args
- Dir.chdir @spec.full_gem_path do
- options = ::RDoc::Options.new
- options.default_title = "#{@spec.full_name} Documentation"
- options.parse args
- options.quiet = !Gem.configuration.really_verbose
- end
+ Dir.chdir @spec.full_gem_path do
+ options = Options.new
+ options.default_title = "#{@spec.full_name} Documentation"
+ options.parse args
+ options.quiet = !Gem.configuration.really_verbose
+ end
- @rdoc = new_rdoc
+ @rdoc = new_rdoc
- say "Parsing documentation for #{@spec.full_name}"
+ say "Parsing documentation for #{@spec.full_name}"
- Dir.chdir @spec.full_gem_path do
- # RDoc::Options#finish must be called before parse_files.
- # RDoc::Options#finish is also called after ri/aliki generator setup.
- # We need to dup the options to avoid modifying it after finish is called.
- parse_options = options.dup
- parse_options.finish
- @rdoc.options = parse_options
- @rdoc.store = RDoc::Store.new(parse_options)
- @rdoc.parse_files parse_options.files
- end
+ Dir.chdir @spec.full_gem_path do
+ # RDoc::Options#finish must be called before parse_files.
+ # RDoc::Options#finish is also called after ri/aliki generator setup.
+ # We need to dup the options to avoid modifying it after finish is called.
+ parse_options = options.dup
+ parse_options.finish
+ @rdoc.options = parse_options
+ @rdoc.store = Store.new(parse_options)
+ @rdoc.parse_files parse_options.files
+ end
- document 'ri', options, @ri_dir if
- @generate_ri and (@force or not File.exist? @ri_dir)
+ document 'ri', options, @ri_dir if
+ @generate_ri and (@force or not File.exist? @ri_dir)
- document 'aliki', options, @rdoc_dir if
- @generate_rdoc and (@force or not File.exist? @rdoc_dir)
- end
+ document 'aliki', options, @rdoc_dir if
+ @generate_rdoc and (@force or not File.exist? @rdoc_dir)
+ end
- ##
- # #new_rdoc creates a new RDoc instance. This method is provided only to
- # make testing easier.
+ ##
+ # #new_rdoc creates a new RDoc instance. This method is provided only to
+ # make testing easier.
- def new_rdoc # :nodoc:
- ::RDoc::RDoc.new
- end
+ def new_rdoc # :nodoc:
+ RDoc.new
+ end
- ##
- # Is rdoc documentation installed?
+ ##
+ # Is rdoc documentation installed?
- def rdoc_installed?
- File.exist? @rdoc_dir
- end
+ def rdoc_installed?
+ File.exist? @rdoc_dir
+ end
- ##
- # Removes generated RDoc and ri data
+ ##
+ # Removes generated RDoc and ri data
- def remove
- base_dir = @spec.base_dir
+ def remove
+ base_dir = @spec.base_dir
- raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
+ raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
- FileUtils.rm_rf @rdoc_dir
- FileUtils.rm_rf @ri_dir
- end
+ FileUtils.rm_rf @rdoc_dir
+ FileUtils.rm_rf @ri_dir
+ end
- ##
- # Is ri data installed?
+ ##
+ # Is ri data installed?
- def ri_installed?
- File.exist? @ri_dir
- end
+ def ri_installed?
+ File.exist? @ri_dir
+ end
- ##
- # Prepares the spec for documentation generation
+ ##
+ # Prepares the spec for documentation generation
- def setup
- self.class.load_rdoc
+ def setup
+ self.class.load_rdoc
- raise Gem::FilePermissionError, @doc_dir if
- File.exist?(@doc_dir) and not File.writable?(@doc_dir)
+ raise Gem::FilePermissionError, @doc_dir if
+ File.exist?(@doc_dir) and not File.writable?(@doc_dir)
- FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
- end
+ FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
+ end
+ end
end
module RDoc
diff --git a/lib/rdoc/server.rb b/lib/rdoc/server.rb
index a5b4862f6b..ee8021c7c9 100644
--- a/lib/rdoc/server.rb
+++ b/lib/rdoc/server.rb
@@ -6,26 +6,27 @@
require 'set'
require 'uri'
-##
-# A minimal HTTP server for live-reloading RDoc documentation.
-#
-# Uses Ruby's built-in +TCPServer+ (no external dependencies).
-#
-# Used by rdoc --server to let developers preview documentation
-# while editing source files. Parses sources once on startup, watches for
-# file changes, re-parses only the changed files, and auto-refreshes the
-# browser via a simple polling script.
-
-class RDoc::Server
-
+module RDoc
##
- # Returns a live-reload polling script with the given +last_change_time+
- # embedded so the browser knows the exact timestamp of the content it
- # received. This avoids a race where a change that occurs between page
- # generation and the first poll would be silently skipped.
+ # A minimal HTTP server for live-reloading RDoc documentation.
+ #
+ # Uses Ruby's built-in +TCPServer+ (no external dependencies).
+ #
+ # Used by rdoc --server to let developers preview documentation
+ # while editing source files. Parses sources once on startup, watches for
+ # file changes, re-parses only the changed files, and auto-refreshes the
+ # browser via a simple polling script.
+
+ class Server
- def self.live_reload_script(last_change_time)
- <<~JS
+ ##
+ # Returns a live-reload polling script with the given +last_change_time+
+ # embedded so the browser knows the exact timestamp of the content it
+ # received. This avoids a race where a change that occurs between page
+ # generation and the first poll would be silently skipped.
+
+ def self.live_reload_script(last_change_time)
+ <<~JS
JS
- end
-
- CONTENT_TYPES = {
- '.html' => 'text/html',
- '.css' => 'text/css',
- '.js' => 'application/javascript',
- '.json' => 'application/json',
- }.freeze
-
- STATUS_TEXTS = {
- 200 => 'OK',
- 400 => 'Bad Request',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 500 => 'Internal Server Error',
- }.freeze
-
- class FileChanges # :nodoc:
- attr_reader :changed_files, :removed_files
-
- def initialize(rdoc)
- @rdoc = rdoc
- @changed_files = []
- @removed_files = []
- @reload_rbs_signatures = false
end
- def record_changed(file)
- reload_rbs_signatures_if_needed file
- changed_files << file
- end
+ CONTENT_TYPES = {
+ '.html' => 'text/html',
+ '.css' => 'text/css',
+ '.js' => 'application/javascript',
+ '.json' => 'application/json',
+ }.freeze
+
+ STATUS_TEXTS = {
+ 200 => 'OK',
+ 400 => 'Bad Request',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 500 => 'Internal Server Error',
+ }.freeze
+
+ class FileChanges # :nodoc:
+ attr_reader :changed_files, :removed_files
+
+ def initialize(rdoc)
+ @rdoc = rdoc
+ @changed_files = []
+ @removed_files = []
+ @reload_rbs_signatures = false
+ end
- def record_removed(file)
- reload_rbs_signatures_if_needed file
- removed_files << file
- end
+ def record_changed(file)
+ reload_rbs_signatures_if_needed file
+ changed_files << file
+ end
- def reload_rbs_signatures?
- @reload_rbs_signatures
- end
+ def record_removed(file)
+ reload_rbs_signatures_if_needed file
+ removed_files << file
+ end
- def source_files_changed?
- !changed_files.empty? || !removed_files.empty?
- end
+ def reload_rbs_signatures?
+ @reload_rbs_signatures
+ end
+
+ def source_files_changed?
+ !changed_files.empty? || !removed_files.empty?
+ end
- private
+ private
- def reload_rbs_signatures_if_needed(file)
- @reload_rbs_signatures = true if @rdoc.auto_discovered_rbs_signature_file?(file)
+ def reload_rbs_signatures_if_needed(file)
+ @reload_rbs_signatures = true if @rdoc.auto_discovered_rbs_signature_file?(file)
+ end
end
- end
- ##
- # Creates a new server.
- #
- # +rdoc+ is the RDoc::RDoc instance that has already parsed the source
- # files.
- # +port+ is the TCP port to listen on.
-
- def initialize(rdoc, port)
- @rdoc = rdoc
- @options = rdoc.options
- @store = rdoc.store
- @port = port
-
- # Silence stats output — the server prints its own timing.
- @rdoc.stats.verbosity = 0
- @generator = create_generator
- @template_dir = File.expand_path(@generator.template_dir)
- @page_cache = {}
- @last_change_time = Time.now.to_f
- @mutex = Mutex.new
- @running = false
- end
+ ##
+ # Creates a new server.
+ #
+ # +rdoc+ is the RDoc::RDoc instance that has already parsed the source
+ # files.
+ # +port+ is the TCP port to listen on.
- ##
- # Starts the server. Blocks until interrupted.
+ def initialize(rdoc, port)
+ @rdoc = rdoc
+ @options = rdoc.options
+ @store = rdoc.store
+ @port = port
+
+ # Silence stats output — the server prints its own timing.
+ @rdoc.stats.verbosity = 0
+ @generator = create_generator
+ @template_dir = File.expand_path(@generator.template_dir)
+ @page_cache = {}
+ @last_change_time = Time.now.to_f
+ @mutex = Mutex.new
+ @running = false
+ end
- def start
- @tcp_server = TCPServer.new('127.0.0.1', @port)
- @running = true
+ ##
+ # Starts the server. Blocks until interrupted.
- @watcher_thread = start_watcher(@rdoc.watch_files)
+ def start
+ @tcp_server = TCPServer.new('127.0.0.1', @port)
+ @running = true
- url = "http://localhost:#{@port}"
- $stderr.puts "\nServing documentation at: \e]8;;#{url}\e\\#{url}\e]8;;\e\\"
- $stderr.puts "Press Ctrl+C to stop.\n\n"
+ @watcher_thread = start_watcher(@rdoc.watch_files)
- loop do
- client = @tcp_server.accept
- Thread.new(client) { |c| handle_client(c) }
+ url = "http://localhost:#{@port}"
+ $stderr.puts "\nServing documentation at: \e]8;;#{url}\e\\#{url}\e]8;;\e\\"
+ $stderr.puts "Press Ctrl+C to stop.\n\n"
+
+ loop do
+ client = @tcp_server.accept
+ Thread.new(client) { |c| handle_client(c) }
+ end
+ rescue Interrupt
+ # Ctrl+C
+ ensure
+ @running = false
+ @tcp_server&.close
+ @watcher_thread&.join(2)
end
- rescue Interrupt
- # Ctrl+C
- ensure
- @running = false
- @tcp_server&.close
- @watcher_thread&.join(2)
- end
private
- def measure
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- yield
- ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(1)
- end
+ def measure
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ yield
+ ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(1)
+ end
- def create_generator
- gen = RDoc::Generator::Aliki.new(@store, @options)
- gen.file_output = false
- gen.asset_rel_path = ''
- gen.setup
- gen
- end
+ def create_generator
+ gen = Generator::Aliki.new(@store, @options)
+ gen.file_output = false
+ gen.asset_rel_path = ''
+ gen.setup
+ gen
+ end
- ##
- # Reads an HTTP request from +client+ and dispatches to the router.
+ ##
+ # Reads an HTTP request from +client+ and dispatches to the router.
- def handle_client(client)
- client.binmode
+ def handle_client(client)
+ client.binmode
- return unless IO.select([client], nil, nil, 5)
+ return unless IO.select([client], nil, nil, 5)
- request_line = client.gets("\n")
- return unless request_line
+ request_line = client.gets("\n")
+ return unless request_line
- method, request_uri, = request_line.split(' ', 3)
- return write_response(client, 400, 'text/plain', 'Bad Request') unless request_uri
+ method, request_uri, = request_line.split(' ', 3)
+ return write_response(client, 400, 'text/plain', 'Bad Request') unless request_uri
- begin
- path = URI.parse(request_uri).path
- rescue URI::InvalidURIError
- return write_response(client, 400, 'text/plain', 'Bad Request')
- end
+ begin
+ path = URI.parse(request_uri).path
+ rescue URI::InvalidURIError
+ return write_response(client, 400, 'text/plain', 'Bad Request')
+ end
- while (line = client.gets("\n"))
- break if line.strip.empty?
- end
+ while (line = client.gets("\n"))
+ break if line.strip.empty?
+ end
- unless method == 'GET'
- return write_response(client, 405, 'text/plain', 'Method Not Allowed')
- end
+ unless method == 'GET'
+ return write_response(client, 405, 'text/plain', 'Method Not Allowed')
+ end
- if path.start_with?('/__') || %r{\A/(?:css|js)/}.match?(path)
- status, content_type, body = route(path)
- else
- duration_ms = measure do
+ if path.start_with?('/__') || %r{\A/(?:css|js)/}.match?(path)
status, content_type, body = route(path)
+ else
+ duration_ms = measure do
+ status, content_type, body = route(path)
+ end
+ $stderr.puts "#{status} #{path} (#{duration_ms}ms)"
end
- $stderr.puts "#{status} #{path} (#{duration_ms}ms)"
- end
- write_response(client, status, content_type, body)
- rescue => e
- write_response(client, 500, 'text/html', <<~HTML)
+ write_response(client, status, content_type, body)
+ rescue => e
+ write_response(client, 500, 'text/html', <<~HTML)
Internal Server Error
#{ERB::Util.html_escape e.message}\n#{ERB::Util.html_escape e.backtrace.join("\n")}
HTML
- ensure
- client.close rescue nil
- end
+ ensure
+ client.close rescue nil
+ end
- ##
- # Routes a request path and returns [status, content_type, body].
-
- def route(path)
- case path
- when '/__status'
- t = @mutex.synchronize { @last_change_time }
- [200, 'application/json', JSON.generate(last_change: t)]
- when '/js/search_data.js'
- # Search data is dynamically generated, not a static asset
- serve_page(path)
- when %r{\A/(?:css|js)/}
- serve_asset(path)
- else
- serve_page(path)
+ ##
+ # Routes a request path and returns [status, content_type, body].
+
+ def route(path)
+ case path
+ when '/__status'
+ t = @mutex.synchronize { @last_change_time }
+ [200, 'application/json', JSON.generate(last_change: t)]
+ when '/js/search_data.js'
+ # Search data is dynamically generated, not a static asset
+ serve_page(path)
+ when %r{\A/(?:css|js)/}
+ serve_asset(path)
+ else
+ serve_page(path)
+ end
end
- end
- ##
- # Writes an HTTP/1.1 response to +client+.
-
- def write_response(client, status, content_type, body)
- body_bytes = body.b
-
- header = +"HTTP/1.1 #{status} #{STATUS_TEXTS[status] || 'Unknown'}\r\n"
- header << "Content-Type: #{content_type}\r\n"
- header << "Content-Length: #{body_bytes.bytesize}\r\n"
- header << "Connection: close\r\n"
- header << "\r\n"
-
- client.write(header)
- client.write(body_bytes)
- client.flush
- rescue Errno::EPIPE
- # Client disconnected before we finished writing — harmless.
- end
+ ##
+ # Writes an HTTP/1.1 response to +client+.
- ##
- # Serves a static asset (CSS, JS) from the Aliki template directory.
+ def write_response(client, status, content_type, body)
+ body_bytes = body.b
- def serve_asset(path)
- rel_path = path.delete_prefix("/")
- asset_path = File.join(@generator.template_dir, rel_path)
- real_asset = File.expand_path(asset_path)
+ header = +"HTTP/1.1 #{status} #{STATUS_TEXTS[status] || 'Unknown'}\r\n"
+ header << "Content-Type: #{content_type}\r\n"
+ header << "Content-Length: #{body_bytes.bytesize}\r\n"
+ header << "Connection: close\r\n"
+ header << "\r\n"
- unless real_asset.start_with?("#{@template_dir}/") && File.file?(real_asset)
- return [404, 'text/plain', "Asset not found: #{rel_path}"]
+ client.write(header)
+ client.write(body_bytes)
+ client.flush
+ rescue Errno::EPIPE
+ # Client disconnected before we finished writing — harmless.
end
- ext = File.extname(rel_path)
- content_type = CONTENT_TYPES[ext] || 'application/octet-stream'
- [200, content_type, File.read(real_asset)]
- end
+ ##
+ # Serves a static asset (CSS, JS) from the Aliki template directory.
- ##
- # Serves an HTML page, rendering from the generator or returning a cached
- # version.
+ def serve_asset(path)
+ rel_path = path.delete_prefix("/")
+ asset_path = File.join(@generator.template_dir, rel_path)
+ real_asset = File.expand_path(asset_path)
- def serve_page(path)
- name = path.delete_prefix("/")
- name = 'index.html' if name.empty?
-
- html = render_page(name)
+ unless real_asset.start_with?("#{@template_dir}/") && File.file?(real_asset)
+ return [404, 'text/plain', "Asset not found: #{rel_path}"]
+ end
- unless html
- not_found = @generator.generate_servlet_not_found(
- "The page #{ERB::Util.html_escape path} was not found"
- )
- t = @mutex.synchronize { @last_change_time }
- return [404, 'text/html', inject_live_reload(not_found || '', t)]
+ ext = File.extname(rel_path)
+ content_type = CONTENT_TYPES[ext] || 'application/octet-stream'
+ [200, content_type, File.read(real_asset)]
end
- ext = File.extname(name)
- content_type = CONTENT_TYPES[ext] || 'text/html'
- [200, content_type, html]
- end
+ ##
+ # Serves an HTML page, rendering from the generator or returning a cached
+ # version.
- ##
- # Renders a page through the Aliki generator and caches the result.
+ def serve_page(path)
+ name = path.delete_prefix("/")
+ name = 'index.html' if name.empty?
- def render_page(name)
- @mutex.synchronize do
- return @page_cache[name] if @page_cache[name]
+ html = render_page(name)
- result = generate_page(name)
- return nil unless result
+ unless html
+ not_found = @generator.generate_servlet_not_found(
+ "The page #{ERB::Util.html_escape path} was not found"
+ )
+ t = @mutex.synchronize { @last_change_time }
+ return [404, 'text/html', inject_live_reload(not_found || '', t)]
+ end
- result = inject_live_reload(result, @last_change_time) if name.end_with?('.html')
- @page_cache[name] = result
+ ext = File.extname(name)
+ content_type = CONTENT_TYPES[ext] || 'text/html'
+ [200, content_type, html]
end
- end
- ##
- # Dispatches to the appropriate generator method based on the page name.
-
- def generate_page(name)
- case name
- when 'index.html'
- @generator.generate_index
- when 'table_of_contents.html'
- @generator.generate_table_of_contents
- when 'js/search_data.js'
- "var search_data = #{JSON.generate(index: @generator.build_search_index)};"
- else
- text_name = name.chomp('.html')
- class_name = text_name.gsub('/', '::')
-
- if klass = @store.find_class_or_module(class_name)
- @generator.generate_class(klass)
- elsif page = @store.find_text_page(text_name.sub(/_([^_]*)\z/, '.\1'))
- @generator.generate_page(page)
+ ##
+ # Renders a page through the Aliki generator and caches the result.
+
+ def render_page(name)
+ @mutex.synchronize do
+ return @page_cache[name] if @page_cache[name]
+
+ result = generate_page(name)
+ return nil unless result
+
+ result = inject_live_reload(result, @last_change_time) if name.end_with?('.html')
+ @page_cache[name] = result
end
end
- end
- ##
- # Injects the live-reload polling script before ++.
+ ##
+ # Dispatches to the appropriate generator method based on the page name.
+
+ def generate_page(name)
+ case name
+ when 'index.html'
+ @generator.generate_index
+ when 'table_of_contents.html'
+ @generator.generate_table_of_contents
+ when 'js/search_data.js'
+ "var search_data = #{JSON.generate(index: @generator.build_search_index)};"
+ else
+ text_name = name.chomp('.html')
+ class_name = text_name.gsub('/', '::')
+
+ if klass = @store.find_class_or_module(class_name)
+ @generator.generate_class(klass)
+ elsif page = @store.find_text_page(text_name.sub(/_([^_]*)\z/, '.\1'))
+ @generator.generate_page(page)
+ end
+ end
+ end
- def inject_live_reload(html, last_change_time)
- html.sub('', "#{self.class.live_reload_script(last_change_time)}