def handle_regexp_HARD_BREAK target
'" block_quote.parts.each do |part| part.accept self end @res << "\n" end ## # Adds +paragraph+ to the output def accept_paragraph paragraph @res << "\n
" text = paragraph.text @hard_break text = text.gsub(/\r?\n/, ' ') @res << to_html(text) @res << "
\n" end ## # Adds +verbatim+ to the output def accept_verbatim verbatim text = verbatim.text.rstrip klass = nil content = if verbatim.ruby? or parseable? text then begin tokens = RDoc::Parser::RipperStateLex.parse text klass = ' class="ruby"' result = RDoc::TokenStream.to_html tokens result = result + "\n" unless "\n" == result[-1] result rescue CGI.escapeHTML text end else CGI.escapeHTML text end if @options.pipe then @res << "\n#{CGI.escapeHTML text}\n\n"
else
@res << "\n#{content}\n"
end
end
##
# Adds +rule+ to the output
def accept_rule rule
@res << "| ' << to_html(text) << " | \n" end @res << "
|---|
| ' << to_html(text) << " | \n" end @res << "
", ""
add_tag :EM, "", ""
end
##
# Returns the HTML tag for +list_type+, possible using a label from
# +list_item+
def list_item_start(list_item, list_type)
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
"#{to_html heading.text}\n" add_paragraph end ## # Raw sections are untrusted and ignored alias accept_raw ignore ## # Rules are ignored alias accept_rule ignore def accept_paragraph paragraph para = @in_list_entry.last || "
"
text = paragraph.text @hard_break
@res << "#{para}#{to_html text}\n"
add_paragraph
end
##
# Finishes consumption of +list_item+
def accept_list_item_end list_item
end
##
# Prepares the visitor for consuming +list_item+
def accept_list_item_start list_item
@res << list_item_start(list_item, @list.last)
end
##
# 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
##
# Adds +verbatim+ to the output
def accept_verbatim verbatim
throw :done if @characters >= @character_limit
input = verbatim.text.rstrip
text = truncate input
text << ' ...' unless text == input
super RDoc::Markup::Verbatim.new text
add_paragraph
end
##
# Prepares the visitor for HTML snippet generation
def start_accepting
super
@characters = 0
end
##
# Removes escaping from the cross-references in +target+
def handle_regexp_CROSSREF target
target.text.sub(/\A\\/, '')
end
##
# +target+ is a
def handle_regexp_HARD_BREAK target
@characters -= 4
'
'
end
##
# Lists are paragraphs, but notes and labels have a separator
def list_item_start list_item, list_type
throw :done if @characters >= @character_limit
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
"
" when :LABEL, :NOTE then labels = Array(list_item.label).map do |label| to_html label end.join ', ' 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
##
# Returns just the text of +link+, +url+ is only used to determine the link
# type.
def gen_url url, text
if url =~ /^rdoc-label:([^:]*)(?::(.*))?/ then
type = "link"
elsif url =~ /([A-Za-z]+):(.*)/ then
type = $1
else
type = "http"
end
if (type == "http" or type == "https" or type == "link") and
url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
''
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
##
# Converts flow items +flow+
def convert_flow flow
throw :done if @characters >= @character_limit
res = []
@mask = 0
flow.each do |item|
case item
when RDoc::Markup::AttrChanger then
off_tags res, item
on_tags res, item
when String then
text = convert_string item
res << truncate(text)
when RDoc::Markup::RegexpHandling then
text = convert_regexp_handling item
res << truncate(text)
else
raise "Unknown flow element: #{item.inspect}"
end
if @characters >= @character_limit then
off_tags res, RDoc::Markup::AttrChanger.new(0, @mask)
break
end
end
res << ' ...' if @characters >= @character_limit
res.join
end
##
# Maintains a bitmask to allow HTML elements to be closed properly. See
# RDoc::Markup::Formatter.
def on_tags res, item
@mask ^= item.turn_on
super
end
##
# Maintains a bitmask to allow HTML elements to be closed properly. See
# RDoc::Markup::Formatter.
def off_tags res, item
@mask ^= item.turn_off
super
end
##
# Truncates +text+ at the end of the first word after the character_limit.
def truncate text
length = text.length
characters = @characters
@characters += length
return text if @characters < @character_limit
remaining = @character_limit - characters
text =~ /\A(.{#{remaining},}?)(\s|$)/m # TODO word-break instead of \s?
$1
end
end
PK ! Т markup/indented_paragraph.rbnu [ # frozen_string_literal: true
##
# An Indented Paragraph of text
class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
##
# The indent in number of spaces
attr_reader :indent
##
# Creates a new IndentedParagraph containing +parts+ indented with +indent+
# spaces
def initialize indent, *parts
@indent = indent
super(*parts)
end
def == other # :nodoc:
super and indent == other.indent
end
##
# 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 text hard_break = nil
@parts.map do |part|
if RDoc::Markup::HardBreak === part then
'%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break
else
part
end
end.join
end
end
PK ! t{l l markup/to_markdown.rbnu [ # frozen_string_literal: true
# :markup: markdown
##
# Outputs parsed markup as Markdown
class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
##
# Creates a new formatter that will output Markdown format text
def initialize markup = nil
super
@headings[1] = ['# ', '']
@headings[2] = ['## ', '']
@headings[3] = ['### ', '']
@headings[4] = ['#### ', '']
@headings[5] = ['##### ', '']
@headings[6] = ['###### ', '']
add_regexp_handling_RDOCLINK
add_regexp_handling_TIDYLINK
@hard_break = " \n"
end
##
# Maps attributes to HTML sequences
def init_tags
add_tag :BOLD, '**', '**'
add_tag :EM, '*', '*'
add_tag :TT, '`', '`'
end
##
# Adds a newline to the output
def handle_regexp_HARD_BREAK target
" \n"
end
##
# Finishes consumption of `list`
def accept_list_end list
@res << "\n"
super
end
##
# Finishes consumption of `list_item`
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
4
when :NOTE, :LABEL then
use_prefix
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 then
bullets = Array(list_item.label).map do |label|
attributes(label).strip
end.join "\n"
bullets << "\n:"
@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
##
# Prepares the visitor for consuming `list`
def accept_list_start list
case list.type
when :BULLET, :LABEL, :NOTE then
@list_index << nil
when :LALPHA, :NUMBER, :UALPHA then
@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:/ then
$'
when /^rdoc-label:footmark-(\d+)/ then
"[^#{$1}]:"
when /^rdoc-label:foottext-(\d+)/ then
"[^#{$1}]"
when /^rdoc-label:label-/ then
gen_url url, $'
when /^rdoc-image:/ then
""
when /^rdoc-[a-z]+:/ then
$'
end
end
##
# Converts the RDoc markup tidylink into a Markdown.style link.
def handle_regexp_TIDYLINK target
text = target.text
return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
label = $1
url = $2
if url =~ /^rdoc-label:foot/ then
handle_rdoc_link url
else
gen_url url, label
end
end
##
# Converts the rdoc-...: links into a Markdown.style links.
def handle_regexp_RDOCLINK target
handle_rdoc_link target.text
end
end
PK ! fQ`6 6 markup/to_ansi.rbnu [ # frozen_string_literal: true
##
# Outputs RDoc markup with vibrant ANSI color!
class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
##
# Creates a new ToAnsi visitor that is ready to output vibrant ANSI color!
def initialize markup = nil
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
def init_tags
add_tag :BOLD, "\e[1m", "\e[m"
add_tag :TT, "\e[7m", "\e[m"
add_tag :EM, "\e[4m", "\e[m"
end
##
# Overrides indent width to ensure output lines up correctly.
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
2
when :NOTE, :LABEL then
if @prefix then
@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 then
'*'
when :NOTE, :LABEL then
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 then
@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
##
# Starts accepting with a reset screen
def start_accepting
super
@res = ["\e[0m"]
end
end
PK ! [ [ markup/to_label.rbnu [ # frozen_string_literal: true
require 'cgi/util'
##
# 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
attr_reader :res # :nodoc:
##
# Creates a new formatter that will output HTML-safe labels
def initialize markup = nil
super nil, markup
@markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
@markup.add_regexp_handling(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK)
add_tag :BOLD, '', ''
add_tag :TT, '', ''
add_tag :EM, '', ''
@res = []
end
##
# Converts +text+ to an HTML-safe label
def convert text
label = convert_flow @am.flow text
CGI.escape(label).gsub('%', '-').sub(/^-/, '')
end
##
# Converts the CROSSREF +target+ to plain text, removing the suppression
# marker, if any
def handle_regexp_CROSSREF target
text = target.text
text.sub(/^\\/, '')
end
##
# Converts the TIDYLINK +target+ to just the text part
def handle_regexp_TIDYLINK target
text = target.text
return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
$1
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 handle_regexp_HARD_BREAK ignore
alias start_accepting ignore
end
PK ! C"= = markup/include.rbnu [ # 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.
class RDoc::Markup::Include
##
# The filename to be included, without extension
attr_reader :file
##
# Directories to search for #file
attr_reader :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 == 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
end
PK ! WY markup/document.rbnu [ # frozen_string_literal: true
##
# A Document containing lists, headings, paragraphs, etc.
class RDoc::Markup::Document
include Enumerable
##
# The file this document was created from. See also
# RDoc::ClassModule#add_comment
attr_reader :file
##
# If a heading is below the given level it will be omitted from the
# table_of_contents
attr_accessor :omit_headings_below
##
# The parts of the Document
attr_reader :parts
##
# Creates a new Document with +parts+
def initialize *parts
@parts = []
@parts.concat parts
@file = nil
@omit_headings_from_table_of_contents_below = nil
end
##
# Appends +part+ to the document
def << part
case part
when RDoc::Markup::Document then
unless part.empty? then
parts.concat part.parts
parts << RDoc::Markup::BlankLine.new
end
when String then
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
##
# Runs this document and all its #items through +visitor+
def accept visitor
visitor.start_accepting
visitor.accept_document self
visitor.end_accepting
end
##
# Concatenates the given +parts+ onto the document
def concat parts
self.parts.concat parts
end
##
# Enumerator for the parts of this document
def each &block
@parts.each(&block)
end
##
# Does this document have no parts?
def empty?
@parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?)
end
##
# The file this Document was created from.
def file= location
@file = case location
when RDoc::TopLevel then
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? then
@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
##
# Does this Document contain other Documents?
def merged?
RDoc::Markup::Document === @parts.first
end
def pretty_print q # :nodoc:
start = @file ? "[doc (#{@file}): " : '[doc: '
q.group 2, start, ']' do
q.seplist @parts do |part|
q.pp part
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.
def table_of_contents
accept RDoc::Markup::ToTableOfContents.to_toc
end
end
PK ! fcr markup/list_item.rbnu [ # frozen_string_literal: true
##
# An item within a List that contains paragraphs, headings, etc.
#
# For BULLET, NUMBER, LALPHA and UALPHA lists, the label will always be nil.
# For NOTE and LABEL lists, the list label may contain:
#
# * a single String for a single label
# * an Array of Strings for a list item with multiple terms
# * nil for an extra description attached to a previously labeled list item
class RDoc::Markup::ListItem
##
# The label for the ListItem
attr_accessor :label
##
# Parts of the ListItem
attr_reader :parts
##
# Creates a new ListItem with an optional +label+ containing +parts+
def initialize label = nil, *parts
@label = label
@parts = []
@parts.concat parts
end
##
# Appends +part+ to the ListItem
def << part
@parts << part
end
def == other # :nodoc:
self.class == other.class and
@label == other.label and
@parts == other.parts
end
##
# Runs this list item and all its #parts through +visitor+
def accept visitor
visitor.accept_list_item_start self
@parts.each do |part|
part.accept visitor
end
visitor.accept_list_item_end self
end
##
# Is the ListItem empty?
def empty?
@parts.empty?
end
##
# Length of parts in the ListItem
def length
@parts.length
end
def pretty_print q # :nodoc:
q.group 2, '[item: ', ']' do
case @label
when Array then
q.pp @label
q.text ';'
q.breakable
when String then
q.pp @label
q.text ';'
q.breakable
end
q.seplist @parts do |part|
q.pp part
end
end
end
##
# Adds +parts+ to the ListItem
def push *parts
@parts.concat parts
end
end
PK ! ; ; markup/rule.rbnu [ # frozen_string_literal: true
##
# A horizontal rule with a weight
class RDoc::Markup::Rule < Struct.new :weight
##
# Calls #accept_rule on +visitor+
def accept visitor
visitor.accept_rule self
end
def pretty_print q # :nodoc:
q.group 2, '[rule:', ']' do
q.pp weight
end
end
end
PK ! ߦo markup/attr_changer.rbnu [ # frozen_string_literal: true
class RDoc::Markup
AttrChanger = Struct.new :turn_on, :turn_off # :nodoc:
end
##
# An AttrChanger records a change in attributes. It contains a bitmap of the
# attributes to turn on, and a bitmap of those to turn off.
class RDoc::Markup::AttrChanger
def to_s # :nodoc:
"Attr: +#{turn_on}/-#{turn_off}"
end
def inspect # :nodoc:
'+%d/-%d' % [turn_on, turn_off]
end
end
PK ! %F markup/formatter.rbnu [ # frozen_string_literal: true
##
# Base class for RDoc markup formatters
#
# Formatters are a visitor that converts an RDoc::Markup tree (from a comment)
# into some kind of output. RDoc ships with formatters for converting back to
# rdoc, ANSI text, HTML, a Table of Contents and other formats.
#
# If you'd like to write your own Formatter use
# RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter
# use RDoc::Markup::TextFormatterTestCase which provides extra test cases.
class RDoc::Markup::Formatter
##
# Tag for inline markup containing a +bit+ for the bitmask and the +on+ and
# +off+ triggers.
InlineTag = Struct.new(:bit, :on, :off)
##
# 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
from = from.split "/"
to = to.split "/"
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
def initialize options, markup = nil
@options = options
@markup = markup || RDoc::Markup.new
@am = @markup.attribute_manager
@am.add_regexp_handling(/
/, :HARD_BREAK)
@attributes = @am.attributes
@attr_tags = []
@in_tt = 0
@tt_bit = @attributes.bitmap_for :TT
@hard_break = ''
@from_path = '.'
end
##
# Adds +document+ to the output
def accept_document document
document.parts.each do |item|
case item
when RDoc::Markup::Document then # HACK
accept_document item
else
item.accept self
end
end
end
##
# Adds a regexp handling for links of the form rdoc-...:
def add_regexp_handling_RDOCLINK
@markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
end
##
# Adds a regexp handling for links of the form {#{$1}"
code = false
else
text ||= name
end
link lookup, text, code
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(target)
name = target.text
return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
unless @hyperlink_all then
# 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
end
##
# Handles rdoc-ref: scheme links and allows RDoc::Markup::ToHtml to
# handle other schemes.
def handle_regexp_HYPERLINK target
return cross_reference $' if target.text =~ /\Ardoc-ref:/
super
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 target
url = target.text
case url
when /\Ardoc-ref:/ then
cross_reference $'
else
super
end
end
##
# Generates links for rdoc-ref: scheme URLs and allows
# RDoc::Markup::ToHtml to handle other schemes.
def gen_url url, text
return super unless url =~ /\Ardoc-ref:/
name = $'
cross_reference name, text, name == text
end
##
# Creates an HTML link to +name+ with the given +text+.
def link name, text, code = true
if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])@/
name = $1
label = $'
end
ref = @cross_reference.resolve name, text
case ref
when String then
ref
else
path = ref.as_href @from_path
if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref)
text = "#{CGI.escapeHTML text}"
end
if path =~ /#/ then
path << "-label-#{label}"
elsif ref.sections and
ref.sections.any? { |section| label == section.title } then
path << "##{label}"
else
if ref.respond_to?(:aref)
path << "##{ref.aref}-label-#{label}"
else
path << "#label-#{label}"
end
end if label
"#{text}"
end
end
end
PK ! [nA# # markup/verbatim.rbnu [ # frozen_string_literal: true
##
# A section of verbatim text
class RDoc::Markup::Verbatim < RDoc::Markup::Raw
##
# Format of this verbatim section
attr_accessor :format
def initialize *parts # :nodoc:
super
@format = nil
end
def == other # :nodoc:
super and @format == other.format
end
##
# Calls #accept_verbatim on +visitor+
def accept visitor
visitor.accept_verbatim self
end
##
# Collapses 3+ newlines into two newlines
def normalize
parts = []
newlines = 0
@parts.each do |part|
case part
when /^\s*\n/ then
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
def pretty_print q # :nodoc:
self.class.name =~ /.*::(\w{1,4})/i
q.group 2, "[#{$1.downcase}: ", ']' do
if @format then
q.text "format: #{@format}"
q.breakable
end
q.seplist @parts do |part|
q.pp part
end
end
end
##
# Is this verbatim section Ruby code?
def ruby?
@format ||= nil # TODO for older ri data, switch the tree to marshal_dump
@format == :ruby
end
##
# The text of the section
def text
@parts.join
end
end
PK ! | markup/regexp_handling.rbnu [ # frozen_string_literal: true
##
# Hold details of a regexp handling sequence
class RDoc::Markup::RegexpHandling
##
# Regexp handling type
attr_reader :type
##
# Regexp handling text
attr_accessor :text
##
# Creates a new regexp handling sequence of +type+ with +text+
def initialize(type, text)
@type, @text = type, text
end
##
# Regexp handlings are equal when the have the same text and type
def ==(o)
self.text == o.text && self.type == o.type
end
def inspect # :nodoc:
"#.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
##
# 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
if rd.find { |i| /\S/ === i } and !rd.find{|i| /^=begin\b/ === i } then
rd.unshift("=begin\n").push("=end\n")
end
parser = RDoc::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
document
end
autoload :BlockParser, "#{__dir__}/rd/block_parser"
autoload :InlineParser, "#{__dir__}/rd/inline_parser"
autoload :Inline, "#{__dir__}/rd/inline"
end
PK ! T߆ text.rbnu [ # frozen_string_literal: true
##
# For RDoc::Text#to_html
require 'strscan'
##
# Methods for manipulating comment text
module RDoc::Text
attr_accessor :language
##
# Maps markup formats to classes that can parse them. If the format is
# unknown, "rdoc" format is used.
MARKUP_FORMAT = {
'markdown' => RDoc::Markdown,
'rdoc' => RDoc::Markup,
'rd' => RDoc::RD,
'tomdoc' => RDoc::TomDoc,
}
MARKUP_FORMAT.default = RDoc::Markup
##
# Maps an encoding to a Hash of characters properly transcoded for that
# encoding.
#
# See also encode_fallback.
TO_HTML_CHARACTERS = Hash.new do |h, encoding|
h[encoding] = {
:close_dquote => encode_fallback('”', encoding, '"'),
:close_squote => encode_fallback('’', encoding, '\''),
:copyright => encode_fallback('©', encoding, '(c)'),
:ellipsis => encode_fallback('…', encoding, '...'),
:em_dash => encode_fallback('—', encoding, '---'),
:en_dash => encode_fallback('–', encoding, '--'),
:open_dquote => encode_fallback('“', encoding, '"'),
:open_squote => encode_fallback('‘', encoding, '\''),
:trademark => encode_fallback('®', encoding, '(r)'),
}
end
##
# Transcodes +character+ to +encoding+ with a +fallback+ character.
def self.encode_fallback character, encoding, fallback
character.encode(encoding, :fallback => { character => fallback },
:undef => :replace, :replace => fallback)
end
##
# Expands tab characters in +text+ to eight spaces
def expand_tabs text
expanded = []
text.each_line do |line|
nil while line.gsub!(/(?:\G|\r)((?:.{8})*?)([^\t\r\n]{0,7})\t/) do
r = "#{$1}#{$2}#{' ' * (8 - $2.size)}"
r = RDoc::Encoding.change_encoding r, text.encoding
r
end
expanded << line
end
expanded.join
end
##
# Flush +text+ left based on the shortest line
def flush_left text
indent = 9999
text.each_line do |line|
line_indent = line =~ /\S/ || 9999
indent = line_indent if indent > line_indent
end
empty = ''
empty = RDoc::Encoding.change_encoding empty, text.encoding
text.gsub(/^ {0,#{indent}}/, empty)
end
##
# Convert a string in markup format into HTML.
#
# Requires the including class to implement #formatter
def markup text
if @store.rdoc.options
locale = @store.rdoc.options.locale
else
locale = nil
end
if locale
i18n_text = RDoc::I18n::Text.new(text)
text = i18n_text.translate(locale)
end
parse(text).accept formatter
end
##
# Strips hashes, expands tabs then flushes +text+ to the left
def normalize_comment text
return text if text.empty?
case language
when :ruby
text = strip_hashes text
when :c
text = strip_stars text
end
text = expand_tabs text
text = flush_left text
text = strip_newlines text
text
end
##
# Normalizes +text+ then builds a RDoc::Markup::Document from it
def parse text, format = 'rdoc'
return text if RDoc::Markup::Document === text
return text.parse if RDoc::Comment === text
text = normalize_comment text # TODO remove, should not be necessary
return RDoc::Markup::Document.new if text =~ /\A\n*\z/
MARKUP_FORMAT[format].parse text
end
##
# The first +limit+ characters of +text+ as HTML
def snippet text, limit = 100
document = parse text
RDoc::Markup::ToHtmlSnippet.new(options, limit).convert document
end
##
# Strips leading # characters from +text+
def strip_hashes text
return text if text =~ /^(?>\s*)[^\#]/
empty = ''
empty = RDoc::Encoding.change_encoding empty, text.encoding
text.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }.gsub(/^\s+$/, empty)
end
##
# Strips leading and trailing \n characters from +text+
def strip_newlines text
text.gsub(/\A\n*(.*?)\n*\z/m) do $1 end # block preserves String encoding
end
##
# Strips /* */ style comments
def strip_stars text
return text unless text =~ %r%/\*.*\*/%m
encoding = text.encoding
text = text.gsub %r%Document-method:\s+[\w:.#=!?|^&<>~+\-/*\%@`\[\]]+%, ''
space = ' '
space = RDoc::Encoding.change_encoding space, encoding if encoding
text.sub! %r%/\*+% do space * $&.length end
text.sub! %r%\*+/% do space * $&.length end
text.gsub! %r%^[ \t]*\*%m do space * $&.length end
empty = ''
empty = RDoc::Encoding.change_encoding empty, encoding if encoding
text.gsub(/^\s+$/, empty)
end
##
# Converts ampersand, dashes, ellipsis, quotes, copyright and registered
# trademark symbols in +text+ to properly encoded characters.
def to_html text
html = (''.encode text.encoding).dup
encoded = RDoc::Text::TO_HTML_CHARACTERS[text.encoding]
s = StringScanner.new text
insquotes = false
indquotes = false
after_word = nil
until s.eos? do
case
when s.scan(/<(tt|code)>.*?<\/\1>/) then # skip contents of tt
html << s.matched.gsub('\\\\', '\\')
when s.scan(/<(tt|code)>.*?/) then
warn "mismatched <#{s[1]}> tag" # TODO signal file/line
html << s.matched
when s.scan(/<[^>]+\/?s*>/) then # skip HTML tags
html << s.matched
when s.scan(/\\(\S)/) then # unhandled suppressed crossref
html << s[1]
after_word = nil
when s.scan(/\.\.\.(\.?)/) then
html << s[1] << encoded[:ellipsis]
after_word = nil
when s.scan(/\(c\)/i) then
html << encoded[:copyright]
after_word = nil
when s.scan(/\(r\)/i) then
html << encoded[:trademark]
after_word = nil
when s.scan(/---/) then
html << encoded[:em_dash]
after_word = nil
when s.scan(/--/) then
html << encoded[:en_dash]
after_word = nil
when s.scan(/"|"/) then
html << encoded[indquotes ? :close_dquote : :open_dquote]
indquotes = !indquotes
after_word = nil
when s.scan(/``/) then # backtick double quote
html << encoded[:open_dquote]
after_word = nil
when s.scan(/(?:'|'){2}/) then # tick double quote
html << encoded[:close_dquote]
after_word = nil
when s.scan(/`/) then # backtick
if insquotes or after_word
html << '`'
after_word = false
else
html << encoded[:open_squote]
insquotes = true
end
when s.scan(/'|'/) then # single quote
if insquotes
html << encoded[:close_squote]
insquotes = false
elsif after_word
# Mary's dog, my parents' house: do not start paired quotes
html << encoded[:close_squote]
else
html << encoded[:open_squote]
insquotes = true
end
after_word = nil
else # advance to the next potentially significant character
match = s.scan(/.+?(?=[<\\.("'`&-])/) #"
if match then
html << match
after_word = match =~ /\w$/
else
html << s.rest
break
end
end
end
html
end
##
# Wraps +txt+ to +line_len+
def wrap(txt, line_len = 76)
res = []
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res.join.strip
end
end
PK ! Ȅ
anon_class.rbnu [ # frozen_string_literal: true
##
# An anonymous class like:
#
# c = Class.new do end
#
# AnonClass is currently not used.
class RDoc::AnonClass < RDoc::ClassModule
end
PK ! ;
options.rbnu [ # frozen_string_literal: true
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
##
# The deprecated options.
DEPRECATED = {
'--accessor' => 'support discontinued',
'--diagram' => 'support discontinued',
'--help-output' => 'support discontinued',
'--image-format' => 'was an option for --diagram',
'--inline-source' => 'source code is now always inlined',
'--merge' => 'ri now always merges class information',
'--one-file' => 'support discontinued',
'--op-name' => 'support discontinued',
'--opname' => 'support discontinued',
'--promiscuous' => 'files always only document their content',
'--ri-system' => 'Ruby installers use other techniques',
}
##
# 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
op_dir
page_dir
option_parser
pipe
rdoc_include
root
static_path
stylesheet_url
template
template_dir
update_output_dir
verbosity
write_options
]
##
# Option validator for OptionParser that matches a directory that exists on
# the filesystem.
Directory = Object.new
##
# Option validator for OptionParser that matches a file or directory that
# exists on the filesystem.
Path = Object.new
##
# Option validator for OptionParser that matches a comma-separated list of
# files or directories that exist on the filesystem.
PathArray = Object.new
##
# Option validator for OptionParser that matches a template directory for an
# installed generator that lives in
# "rdoc/generator/template/#{template_name}"
Template = Object.new
##
# Character-set for HTML output. #encoding is preferred over #charset
attr_accessor :charset
##
# If true, RDoc will not write any files.
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.
attr_accessor :encoding
##
# Files matching this pattern will be excluded
attr_writer :exclude
##
# The list of files to be processed
attr_accessor :files
##
# Create the output even if the output directory does not look
# like an rdoc output directory
attr_accessor :force_output
##
# Scan newer sources than the flag file if true.
attr_accessor :force_update
##
# Formatter to mark up text with
attr_accessor :formatter
##
# Description of the output generator (set with the --format option)
attr_accessor :generator
##
# For #==
attr_reader :generator_name # :nodoc:
##
# Loaded generator options. Used to prevent --help from loading the same
# options multiple times.
attr_accessor :generator_options
##
# Old rdoc behavior: hyperlink all words that match a method name,
# even if not preceded by '#' or '::'
attr_accessor :hyperlink_all
##
# Include line numbers in the source code
attr_accessor :line_numbers
##
# The output locale.
attr_accessor :locale
##
# The directory where locale data live.
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)
attr_accessor :main_page
##
# The default markup format. The default is 'rdoc'. 'markdown', 'tomdoc'
# and 'rd' are also built-in.
attr_accessor :markup
##
# If true, only report on undocumented files
attr_accessor :coverage_report
##
# The name of the output directory
attr_accessor :op_dir
##
# The OptionParser for this instance
attr_accessor :option_parser
##
# Output heading decorations?
attr_accessor :output_decoration
##
# 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.
attr_accessor :page_dir
##
# Is RDoc in pipe mode?
attr_accessor :pipe
##
# Array of directories to search for files to satisfy an :include:
attr_accessor :rdoc_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 :root
##
# Include the '#' at the front of hyperlinked instance method names
attr_accessor :show_hash
##
# Directory to copy static files from
attr_accessor :static_path
##
# The number of columns in a tab
attr_accessor :tab_width
##
# Template to be used when generating output
attr_accessor :template
##
# Directory the template lives in
attr_accessor :template_dir
##
# Additional template stylesheets
attr_accessor :template_stylesheets
##
# Documentation title
attr_accessor :title
##
# Should RDoc update the timestamps in the output dir?
attr_accessor :update_output_dir
##
# Verbosity, zero means quiet
attr_accessor :verbosity
##
# URL of web cvs frontend
attr_accessor :webcvs
##
# 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_reader :visibility
##
# Indicates if files of test suites should be skipped
attr_accessor :skip_tests
def initialize loaded_options = nil # :nodoc:
init_ivars
override loaded_options if loaded_options
end
def init_ivars # :nodoc:
@dry_run = false
@exclude = %w[
~\z \.orig\z \.rej\z \.bak\z
\.gemspec\z
]
@files = nil
@force_output = false
@force_update = true
@generator = nil
@generator_name = nil
@generator_options = []
@generators = RDoc::RDoc::GENERATORS
@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)
@show_hash = false
@static_path = []
@stylesheet_url = nil # TODO remove in RDoc 4
@tab_width = 8
@template = nil
@template_dir = nil
@template_stylesheets = []
@title = nil
@update_output_dir = true
@verbosity = 1
@visibility = :protected
@webcvs = nil
@write_options = false
@encoding = Encoding::UTF_8
@charset = @encoding.name
@skip_tests = true
end
def init_with map # :nodoc:
init_ivars
encoding = map['encoding']
@encoding = encoding ? Encoding.find(encoding) : encoding
@charset = map['charset']
@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']
@rdoc_include = sanitize_path map['rdoc_include']
@static_path = sanitize_path map['static_path']
end
def yaml_initialize tag, map # :nodoc:
init_with map
end
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')
@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')
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
@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
end
##
# Check that the files on the command line exist
def check_files
@files.delete_if do |file|
if File.exist? file then
if File.readable? file then
false
else
warn "file '#{file}' not readable"
true
end
else
warn "file '#{file}' not found"
true
end
end
end
##
# Ensure only one generator is loaded
def check_generator
if @generator then
raise OptionParser::InvalidOption,
"generator already set to #{@generator_name}"
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.
def default_title=(string)
@title ||= string
end
##
# For dumping YAML
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)
ivars = instance_variables.map { |ivar| ivar.to_s[1..-1] }
ivars -= SPECIAL
ivars.sort.each do |ivar|
yaml[ivar] = instance_variable_get("@#{ivar}")
end
yaml.to_yaml
end
##
# Create a regexp for #exclude
def exclude
if @exclude.nil? or Regexp === @exclude then
# done, #finish is being re-run
@exclude
elsif @exclude.empty? then
nil
else
Regexp.new(@exclude.join("|"))
end
end
##
# 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 then
write_options
exit
end
@op_dir ||= 'doc'
@rdoc_include << "." if @rdoc_include.empty?
root = @root.to_s
@rdoc_include << root unless @rdoc_include.include?(root)
@exclude = self.exclude
finish_page_dir
check_files
# If no template was specified, use the default template for the output
# formatter
unless @template then
@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
end
self
end
##
# 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
@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 = page_dir
end
##
# Returns a properly-space list of generators and their descriptions.
def generator_descriptions
lengths = []
generators = RDoc::RDoc::GENERATORS.map do |name, generator|
lengths << name.length
description = generator::DESCRIPTION if
generator.const_defined? :DESCRIPTION
[name, description]
end
longest = lengths.max
generators.sort.map do |name, description|
if description then
" %-*s - %s" % [longest, name, description]
else
" #{name}"
end
end.join "\n"
end
##
# Parses command line options.
def parse argv
ignore_invalid = true
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
Usage: #{opt.program_name} [options] [names...]
Files are parsed, and the information they contain collected, before any
output is produced. This allows cross references between all files to be
resolved. If a name is a directory, it is traversed. If no names are
specified, all Ruby files in the current directory (and subdirectories) are
processed.
How RDoc generates output depends on the output formatter being used, and on
the options you give.
Options can be specified via the RDOCOPT environment variable, which
functions similar to the RUBYOPT environment variable for ruby.
$ export RDOCOPT="--show-hash"
will make rdoc show hashes in method links by default. Command-line options
always will override those in RDOCOPT.
Available formatters:
#{generator_descriptions}
RDoc understands the following file formats:
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.banner += "\n The following options are deprecated:\n\n"
name_length = DEPRECATED.keys.sort_by { |k| k.length }.last.length
DEPRECATED.sort_by { |k,| k }.each do |name, reason|
opt.banner += " %*1$2$s %3$s\n" % [-name_length, name, reason]
end
opt.accept Template do |template|
template_dir = template_dir_for template
unless template_dir then
$stderr.puts "could not find template #{template}"
nil
else
[template, template_dir]
end
end
opt.accept Directory do |directory|
directory = File.expand_path directory
raise OptionParser::InvalidArgument unless File.directory? directory
directory
end
opt.accept Path do |path|
path = File.expand_path path
raise OptionParser::InvalidArgument unless File.exist? path
path
end
opt.accept PathArray do |paths,|
paths = if paths then
paths.split(',').map { |d| d unless d.empty? }
end
paths.map 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.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.separator nil
opt.on("--locale=NAME",
"Specifies the output locale.") do |value|
@locale_name = value
end
opt.on("--locale-data-dir=DIR",
"Specifies the directory where locale data live.") do |value|
@locale_dir = value
end
opt.separator nil
opt.on("--all", "-a",
"Synonym for --visibility=private.") do |value|
@visibility = :private
end
opt.separator nil
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-skipping-tests", nil,
"Don't skip generating documentation for test and spec files") do |value|
@skip_tests = false
end
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 then
raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
end
unless RDoc::Parser.alias_extension old, new then
raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
end
end
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.separator nil
opt.on("--pipe", "-p",
"Convert RDoc on stdin to HTML") do
@pipe = true
end
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.separator nil
opt.on("--visibility=VISIBILITY", "-V", 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.separator nil
markup_formats = RDoc::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.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.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.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.separator nil
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
@generator_name = value.downcase
setup_generator
end
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.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
@coverage_report = value
@force_update = true if value
end
opt.separator nil
opt.on("--output=DIR", "--op", "-o",
"Set the output directory.") do |value|
@op_dir = value
end
opt.separator nil
opt.on("-d",
"Deprecated --diagram option.",
"Prevents firing debug mode",
"with legacy invocation.") do |value|
end
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.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.separator nil
opt.on("--main=NAME", "-m",
"NAME will be the initial page displayed.") do |value|
@main_page = value
end
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.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.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.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.separator nil
opt.on("--title=TITLE", "-t",
"Set TITLE as the title for HTML output.") do |value|
@title = value
end
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.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.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.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
opt.separator nil
opt.separator "Generic options:"
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.separator nil
opt.on("--[no-]dry-run",
"Don't write any files") do |value|
@dry_run = value
end
opt.separator nil
opt.on("-D", "--[no-]debug",
"Displays lots on internal stuff.") do |value|
$DEBUG_RDOC = value
end
opt.separator nil
opt.on("--[no-]ignore-invalid",
"Ignore invalid options and continue",
"(default true).") do |value|
ignore_invalid = value
end
opt.separator nil
opt.on("--quiet", "-q",
"Don't show progress as we parse.") do |value|
@verbosity = 0
end
opt.separator nil
opt.on("--verbose", "-V",
"Display extra progress as RDoc parses") do |value|
@verbosity = 2
end
opt.separator nil
opt.on("--version", "-v", "print the version") do
puts opt.version
exit
end
opt.separator nil
opt.on("--help", "-h", "Display this help") do
RDoc::RDoc::GENERATORS.each_key do |generator|
setup_generator generator
end
puts opt.help
exit
end
opt.separator nil
end
setup_generator 'darkfish' if
argv.grep(/\A(-f|--fmt|--format|-r|-R|--ri|--ri-site)\b/).empty?
deprecated = []
invalid = []
begin
opts.parse! argv
rescue OptionParser::ParseError => e
if DEPRECATED[e.args.first] then
deprecated << e.args.first
elsif %w[--format --ri -r --ri-site -R].include? e.args.first then
raise
else
invalid << e.args.join(' ')
end
retry
end
unless @generator then
@generator = RDoc::Generator::Darkfish
@generator_name = 'darkfish'
end
if @pipe and not argv.empty? then
@pipe = false
invalid << '-p (with files)'
end
unless quiet then
deprecated.each do |opt|
$stderr.puts 'option ' + opt + ' is deprecated: ' + DEPRECATED[opt]
end
end
unless invalid.empty? then
invalid = "invalid options: #{invalid.join ', '}"
if ignore_invalid then
unless quiet then
$stderr.puts invalid
$stderr.puts '(invalid options are ignored)'
end
else
unless quiet then
$stderr.puts opts
end
$stderr.puts invalid
exit 1
end
end
@files = argv.dup
self
end
##
# Don't display progress as we process the files
def quiet
@verbosity.zero?
end
##
# Set quietness to +bool+
def quiet= bool
@verbosity = bool ? 0 : 1
end
##
# 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
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.
def setup_generator generator_name = @generator_name
@generator = @generators[generator_name]
unless @generator then
raise OptionParser::InvalidArgument,
"Invalid output formatter #{generator_name}"
end
return if @generator_options.include? @generator
@generator_name = generator_name
@generator_options << @generator
if @generator.respond_to? :setup_options then
@option_parser ||= OptionParser.new
@generator.setup_options self
end
end
##
# Finds the template dir for +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
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
end
end
##
# Displays a warning using Kernel#warn if we're being verbose
def warn message
super message if @verbosity > 1
end
##
# Writes the YAML file .rdoc_options to the current directory containing the
# parsed options.
def write_options
RDoc.load_yaml
File.open '.rdoc_options', 'w' do |io|
io.set_encoding Encoding::UTF_8
io.print to_yaml
end
end
##
# 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
RDoc.load_yaml
begin
options = YAML.safe_load File.read('.rdoc_options'), permitted_classes: [RDoc::Options, Symbol]
rescue Psych::SyntaxError
raise RDoc::Error, "#{options_file} is not a valid rdoc options file"
end
return RDoc::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
if Hash === options
# Override the default values with the contents of YAML file.
options = RDoc::Options.new options
end
options
end
end
PK ! ѓ#R) ) stats.rbnu [ # frozen_string_literal: true
##
# RDoc statistics collector which prints a summary and report of a project's
# documentation totals.
class RDoc::Stats
include RDoc::Text
##
# Output level for the coverage report
attr_reader :coverage_level
##
# Count of files parsed during parsing
attr_reader :files_so_far
##
# Total number of files found
attr_reader :num_files
##
# Creates a new Stats that will have +num_files+. +verbosity+ defaults to 1
# which will create an RDoc::Stats::Normal outputter.
def initialize store, num_files, verbosity = 1
@num_files = num_files
@store = store
@coverage_level = 0
@doc_items = nil
@files_so_far = 0
@fully_documented = false
@num_params = 0
@percent_doc = nil
@start = Time.now
@undoc_params = 0
@display = case verbosity
when 0 then Quiet.new num_files
when 1 then Normal.new num_files
else Verbose.new num_files
end
end
##
# Records the parsing of an alias +as+.
def add_alias as
@display.print_alias as
end
##
# Records the parsing of an attribute +attribute+
def add_attribute attribute
@display.print_attribute attribute
end
##
# Records the parsing of a class +klass+
def add_class klass
@display.print_class klass
end
##
# Records the parsing of +constant+
def add_constant constant
@display.print_constant constant
end
##
# Records the parsing of +file+
def add_file(file)
@files_so_far += 1
@display.print_file @files_so_far, file
end
##
# Records the parsing of +method+
def add_method(method)
@display.print_method method
end
##
# Records the parsing of a module +mod+
def add_module(mod)
@display.print_module mod
end
##
# Call this to mark the beginning of parsing for display purposes
def begin_adding
@display.begin_adding
end
##
# Calculates documentation totals and percentages for classes, modules,
# constants, attributes and methods.
def calculate
return if @doc_items
ucm = @store.unique_classes_and_modules
classes = @store.unique_classes.reject { |cm| cm.full_name == 'Object' }
constants = []
ucm.each { |cm| constants.concat cm.constants }
methods = []
ucm.each { |cm| methods.concat cm.method_list }
attributes = []
ucm.each { |cm| attributes.concat cm.attributes }
@num_attributes, @undoc_attributes = doc_stats attributes
@num_classes, @undoc_classes = doc_stats classes
@num_constants, @undoc_constants = doc_stats constants
@num_methods, @undoc_methods = doc_stats methods
@num_modules, @undoc_modules = doc_stats @store.unique_modules
@num_items =
@num_attributes +
@num_classes +
@num_constants +
@num_methods +
@num_modules +
@num_params
@undoc_items =
@undoc_attributes +
@undoc_classes +
@undoc_constants +
@undoc_methods +
@undoc_modules +
@undoc_params
@doc_items = @num_items - @undoc_items
end
##
# Sets coverage report level. Accepted values are:
#
# false or nil:: No report
# 0:: Classes, modules, constants, attributes, methods
# 1:: Level 0 + method parameters
def coverage_level= level
level = -1 unless level
@coverage_level = level
end
##
# Returns the length and number of undocumented items in +collection+.
def doc_stats collection
visible = collection.select { |item| item.display? }
[visible.length, visible.count { |item| not item.documented? }]
end
##
# Call this to mark the end of parsing for display purposes
def done_adding
@display.done_adding
end
##
# The documentation status of this project. +true+ when 100%, +false+ when
# less than 100% and +nil+ when unknown.
#
# Set by calling #calculate
def fully_documented?
@fully_documented
end
##
# A report that says you did a great job!
def great_job
report = RDoc::Markup::Document.new
report << RDoc::Markup::Paragraph.new('100% documentation!')
report << RDoc::Markup::Paragraph.new('Great Job!')
report
end
##
# Calculates the percentage of items documented.
def percent_doc
return @percent_doc if @percent_doc
@fully_documented = (@num_items - @doc_items) == 0
@percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero?
@percent_doc ||= 0
@percent_doc
end
##
# Returns a report on which items are not documented
def report
if @coverage_level > 0 then
extend RDoc::Text
end
if @coverage_level.zero? then
calculate
return great_job if @num_items == @doc_items
end
ucm = @store.unique_classes_and_modules
report = RDoc::Markup::Document.new
report << RDoc::Markup::Paragraph.new('The following items are not documented:')
report << RDoc::Markup::BlankLine.new
ucm.sort.each do |cm|
body = report_class_module(cm) {
[
report_constants(cm),
report_attributes(cm),
report_methods(cm),
].compact
}
report << body if body
end
if @coverage_level > 0 then
calculate
return great_job if @num_items == @doc_items
end
report
end
##
# Returns a report on undocumented attributes in ClassModule +cm+
def report_attributes cm
return if cm.attributes.empty?
report = []
cm.each_attribute do |attr|
next if attr.documented?
line = attr.line ? ":#{attr.line}" : nil
report << " #{attr.definition} :#{attr.name} # in file #{attr.file.full_name}#{line}\n"
report << "\n"
end
report
end
##
# Returns a report on undocumented items in ClassModule +cm+
def report_class_module cm
return if cm.fully_documented? and @coverage_level.zero?
return unless cm.display?
report = RDoc::Markup::Document.new
if cm.in_files.empty? then
report << RDoc::Markup::Paragraph.new("#{cm.definition} is referenced but empty.")
report << RDoc::Markup::Paragraph.new("It probably came from another project. I'm sorry I'm holding it against you.")
return report
elsif cm.documented? then
documented = true
klass = RDoc::Markup::Verbatim.new("#{cm.definition} # is documented\n")
else
report << RDoc::Markup::Paragraph.new('In files:')
list = RDoc::Markup::List.new :BULLET
cm.in_files.each do |file|
para = RDoc::Markup::Paragraph.new file.full_name
list << RDoc::Markup::ListItem.new(nil, para)
end
report << list
report << RDoc::Markup::BlankLine.new
klass = RDoc::Markup::Verbatim.new("#{cm.definition}\n")
end
klass << "\n"
body = yield.flatten # HACK remove #flatten
if body.empty? then
return if documented
klass.parts.pop
else
klass.parts.concat body
end
klass << "end\n"
report << klass
report
end
##
# Returns a report on undocumented constants in ClassModule +cm+
def report_constants cm
return if cm.constants.empty?
report = []
cm.each_constant do |constant|
# TODO constant aliases are listed in the summary but not reported
# figure out what to do here
next if constant.documented? || constant.is_alias_for
line = constant.line ? ":#{constant.line}" : line
report << " # in file #{constant.file.full_name}#{line}\n"
report << " #{constant.name} = nil\n"
report << "\n"
end
report
end
##
# Returns a report on undocumented methods in ClassModule +cm+
def report_methods cm
return if cm.method_list.empty?
report = []
cm.each_method do |method|
next if method.documented? and @coverage_level.zero?
if @coverage_level > 0 then
params, undoc = undoc_params method
@num_params += params
unless undoc.empty? then
@undoc_params += undoc.length
undoc = undoc.map do |param| "+#{param}+" end
param_report = " # #{undoc.join ', '} is not documented\n"
end
end
next if method.documented? and not param_report
line = method.line ? ":#{method.line}" : nil
scope = method.singleton ? 'self.' : nil
report << " # in file #{method.file.full_name}#{line}\n"
report << param_report if param_report
report << " def #{scope}#{method.name}#{method.params}; end\n"
report << "\n"
end
report
end
##
# Returns a summary of the collected statistics.
def summary
calculate
num_width = [@num_files, @num_items].max.to_s.length
undoc_width = [
@undoc_attributes,
@undoc_classes,
@undoc_constants,
@undoc_items,
@undoc_methods,
@undoc_modules,
@undoc_params,
].max.to_s.length
report = RDoc::Markup::Verbatim.new
report << "Files: %*d\n" % [num_width, @num_files]
report << "\n"
report << "Classes: %*d (%*d undocumented)\n" % [
num_width, @num_classes, undoc_width, @undoc_classes]
report << "Modules: %*d (%*d undocumented)\n" % [
num_width, @num_modules, undoc_width, @undoc_modules]
report << "Constants: %*d (%*d undocumented)\n" % [
num_width, @num_constants, undoc_width, @undoc_constants]
report << "Attributes: %*d (%*d undocumented)\n" % [
num_width, @num_attributes, undoc_width, @undoc_attributes]
report << "Methods: %*d (%*d undocumented)\n" % [
num_width, @num_methods, undoc_width, @undoc_methods]
report << "Parameters: %*d (%*d undocumented)\n" % [
num_width, @num_params, undoc_width, @undoc_params] if
@coverage_level > 0
report << "\n"
report << "Total: %*d (%*d undocumented)\n" % [
num_width, @num_items, undoc_width, @undoc_items]
report << "%6.2f%% documented\n" % percent_doc
report << "\n"
report << "Elapsed: %0.1fs\n" % (Time.now - @start)
RDoc::Markup::Document.new report
end
##
# Determines which parameters in +method+ were not documented. Returns a
# total parameter count and an Array of undocumented methods.
def undoc_params method
@formatter ||= RDoc::Markup::ToTtOnly.new
params = method.param_list
params = params.map { |param| param.gsub(/^\*\*?/, '') }
return 0, [] if params.empty?
document = parse method.comment
tts = document.accept @formatter
undoc = params - tts
[params.length, undoc]
end
autoload :Quiet, "#{__dir__}/stats/quiet"
autoload :Normal, "#{__dir__}/stats/normal"
autoload :Verbose, "#{__dir__}/stats/verbose"
end
PK !
i18n.rbnu [ # frozen_string_literal: true
##
# This module provides i18n related features.
module RDoc::I18n
autoload :Locale, "#{__dir__}/i18n/locale"
require_relative 'i18n/text'
end
PK ! &
require.rbnu [ # frozen_string_literal: true
##
# A file loaded by \#require
class RDoc::Require < RDoc::CodeObject
##
# Name of the required file
attr_accessor :name
##
# 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 inspect # :nodoc:
"#<%s:0x%x require '%s' in %s>" % [
self.class,
object_id,
@name,
parent_file_name,
]
end
def to_s # :nodoc:
"require #{name} in: #{parent}"
end
##
# 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']
if tl.nil? and RDoc::TopLevel.all_files.first.full_name =~ %r(^lib/) then
# second chance
tl = RDoc::TopLevel.all_files_hash['lib/' + name + '.rb']
end
tl
end
end
end
PK ! mixin.rbnu [ # frozen_string_literal: true
##
# A Mixin adds features from a module into another context. RDoc::Include and
# RDoc::Extend are both mixins.
class RDoc::Mixin < RDoc::CodeObject
##
# Name of included module
attr_accessor :name
##
# 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
##
# Mixins are sorted by name
def <=> other
return unless self.class === other
name <=> other.name
end
def == other # :nodoc:
self.class === other and @name == other.name
end
alias eql? == # :nodoc:
##
# Full name based on #module
def full_name
m = self.module
RDoc::ClassModule === m ? m.full_name : @name
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
##
# 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.
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]
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
##
# Sets the store for this class or module and its contained code objects.
def store= store
super
@file = @store.add_file @file.full_name if @file
end
def to_s # :nodoc:
"#{self.class.name.downcase} #@name in: #{parent}"
end
end
PK ! ٔy y
context.rbnu [ # 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
##
# Types of methods
TYPES = %w[class instance]
##
# If a context has these titles it will be sorted in this order.
TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
##
# Class/module aliases
attr_reader :aliases
##
# All attr* methods
attr_reader :attributes
##
# Block params to be used in the next MethodAttr parsed under this context
attr_accessor :block_params
##
# Constants defined
attr_reader :constants
##
# Sets the current documentation section of documentation
attr_writer :current_section
##
# Files this context is found in
attr_reader :in_files
##
# Modules this context includes
attr_reader :includes
##
# Modules this context is extended with
attr_reader :extends
##
# Methods defined in this context
attr_reader :method_list
##
# Name of this class excluding namespace. See also full_name
attr_reader :name
##
# Files this context requires
attr_reader :requires
##
# Use this section for the next method, attribute or constant added.
attr_accessor :temporary_section
##
# 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_accessor :unmatched_alias_lists
##
# Aliases that could not be resolved.
attr_reader :external_aliases
##
# Current visibility of this context
attr_accessor :visibility
##
# Current visibility of this line
attr_writer :current_line_visibility
##
# Hash of registered methods. Attributes are also registered here,
# twice if they are RW.
attr_reader :methods_hash
##
# Params to be used in the next MethodAttr parsed under this context
attr_accessor :params
##
# Hash of registered constants.
attr_reader :constants_hash
##
# Creates an unnamed empty context with public current visibility
def initialize
super
@in_files = []
@name ||= "unknown"
@parent = nil
@visibility = :public
@current_section = Section.new self, nil, nil
@sections = { nil => @current_section }
@temporary_section = nil
@classes = {}
@modules = {}
initialize_methods_etc
end
##
# Sets the defaults for methods and so-forth
def initialize_methods_etc
@method_list = []
@attributes = []
@aliases = []
@requires = []
@includes = []
@extends = []
@constants = []
@external_aliases = []
@current_line_visibility = nil
# This Hash maps a method name to a list of unmatched aliases (aliases of
# a method not yet encountered).
@unmatched_alias_lists = {}
@methods_hash = {}
@constants_hash = {}
@params = nil
@store ||= nil
end
##
# Contexts are sorted by full_name
def <=>(other)
return nil unless RDoc::CodeObject === other
full_name <=> other.full_name
end
##
# 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 then
ext = RDoc::Extend.new name, comment
add_extend ext
elsif RDoc::Include == klass then
incl = RDoc::Include.new name, comment
add_include incl
else
raise NotImplementedError, "adding a #{klass} is not implemented"
end
end
##
# Adds +an_alias+ that is automatically resolved
def add_alias an_alias
return an_alias unless @document_self
method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
find_attribute(an_alias.old_name, an_alias.singleton)
if method_attr then
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
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.
def add_attribute attribute
return attribute unless @document_self
# mainly to check for redefinition of an attribute as a method
# TODO find a policy for 'attr_reader :foo' + 'def foo=()'
register = false
key = nil
if attribute.rw.index 'R' then
key = attribute.pretty_name
known = @methods_hash[key]
if known then
known.comment = attribute.comment if known.comment.empty?
elsif registered = @methods_hash[attribute.pretty_name + '='] and
RDoc::Attr === registered then
registered.rw = 'RW'
else
@methods_hash[key] = attribute
register = true
end
end
if attribute.rw.index 'W' then
key = attribute.pretty_name + '='
known = @methods_hash[key]
if known then
known.comment = attribute.comment if known.comment.empty?
elsif registered = @methods_hash[attribute.pretty_name] and
RDoc::Attr === registered then
registered.rw = 'RW'
else
@methods_hash[key] = attribute
register = true
end
end
if register then
attribute.visibility = @visibility
add_to @attributes, attribute
resolve_aliases 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)
#
# 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+)$/ then
full_name = $1
enclosing = top_level
name = full_name.split(/:+/).last
else
full_name = child_name given_name
if full_name =~ /^(.+)::(\w+)$/ then
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 then
# 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)
names = ename.split('::')
enclosing = self
names.each do |n|
enclosing = enclosing.classes_hash[n] ||
enclosing.modules_hash[n] ||
enclosing.add_module(RDoc::NormalModule, n)
end
end
else
name = full_name
enclosing = self
end
end
# fix up superclass
if full_name == 'BasicObject' then
superclass = nil
elsif full_name == 'Object' then
superclass = '::BasicObject'
end
# find the superclass full name
if superclass then
if superclass =~ /^:+/ then
superclass = $' #'
else
if superclass =~ /^(\w+):+(.+)$/ then
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, RDoc::NormalClass, mod.parent if mod
# e.g., Object < Object
superclass = nil if superclass == full_name
end
klass = @store.classes_hash[full_name]
if klass then
# if TopLevel, it may not be registered in the classes:
enclosing.classes_hash[name] = klass
# update the superclass if needed
if superclass then
existing = klass.superclass
existing = existing.full_name unless existing.is_a?(String) if existing
if existing.nil? ||
(existing == 'Object' && superclass != 'Object') then
klass.superclass = superclass
end
end
else
# this is a new class
mod = @store.modules_hash.delete full_name
if mod then
klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
klass.superclass = superclass unless superclass.nil?
else
klass = class_type.new name, superclass
enclosing.add_class_or_module(klass, enclosing.classes_hash,
@store.classes_hash)
end
end
klass.parent = self
klass
end
##
# 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 then
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
if @store.unmatched_constant_alias[mod.full_name] then
to, file = @store.unmatched_constant_alias[mod.full_name]
add_module_alias mod, mod.name, to, file
end
end
mod
end
##
# 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.
def add_constant constant
return constant unless @document_self
# 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]
if known then
known.comment = constant.comment if known.comment.empty?
known.value = constant.value if
known.value.nil? or known.value.strip.empty?
known.is_alias_for ||= constant.is_alias_for
else
@constants_hash[constant.name] = constant
add_to @constants, constant
end
constant
end
##
# Adds included module +include+ which should be an RDoc::Include
def add_include include
add_to @includes, include
include
end
##
# Adds extension module +ext+ which should be an RDoc::Extend
def add_extend ext
add_to @extends, ext
ext
end
##
# 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 then
if @store then # otherwise we are loading
known.comment = method.comment if known.comment.empty?
previously = ", previously in #{known.file}" unless
method.file == known.file
@store.rdoc.options.warn \
"Duplicate method #{known.full_name} in #{method.file}#{previously}"
end
else
@methods_hash[key] = method
if @current_line_visibility
method.visibility, @current_line_visibility = @current_line_visibility, nil
else
method.visibility = @visibility
end
add_to @method_list, method
resolve_aliases method
end
method
end
##
# 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)
mod = @classes[name] || @modules[name]
return mod if mod
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
##
# 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
##
# Adds an alias from +from+ (a class or module) to +name+ which was defined
# in +file+.
def add_module_alias from, from_name, to, file
return from if @done_documenting
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
unless from
@store.unmatched_constant_alias[child_name(from_name)] = [to, file]
return to
end
new_to = from.dup
new_to.name = to.name
new_to.full_name = nil
if new_to.module? then
@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
# 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
new_to
end
##
# Adds +require+ to this context's top level
def add_require(require)
return require unless @document_self
if RDoc::TopLevel === self then
add_to @requires, require
else
parent.add_require require
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] then
section.add_comment comment if comment
else
section = Section.new self, title, comment
@sections[title] = section
end
section
end
##
# Adds +thing+ to the collection +array+
def add_to array, thing
array << thing if @document_self
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
##
# Creates the full name for a child with +name+
def child_name name
if name =~ /^:+/
$' #'
elsif RDoc::TopLevel === self then
name
else
"#{self.full_name}::#{name}"
end
end
##
# Class attributes
def class_attributes
@class_attributes ||= attributes.select { |a| a.singleton }
end
##
# Class methods
def class_method_list
@class_method_list ||= method_list.select { |a| a.singleton }
end
##
# Array of classes in this context
def classes
@classes.values
end
##
# All classes and modules in this namespace
def classes_and_modules
classes + modules
end
##
# Hash of classes keyed by class name
def classes_hash
@classes
end
##
# The current documentation section that new items will be added to. If
# temporary_section is available it will be used.
def current_section
if section = @temporary_section then
@temporary_section = nil
else
section = @current_section
end
section
end
##
# Is part of this thing was defined in +file+?
def defined_in?(file)
@in_files.include?(file)
end
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}"
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.
def each_ancestor # :nodoc:
end
##
# Iterator for attributes
def each_attribute # :yields: attribute
@attributes.each { |a| yield a }
end
##
# Iterator for classes and modules
def each_classmodule(&block) # :yields: module
classes_and_modules.sort.each(&block)
end
##
# Iterator for constants
def each_constant # :yields: constant
@constants.each {|c| yield c}
end
##
# Iterator for included modules
def each_include # :yields: include
@includes.each do |i| yield i end
end
##
# Iterator for extension modules
def each_extend # :yields: extend
@extends.each do |e| yield e end
end
##
# Iterator for methods
def each_method # :yields: method
return enum_for __method__ unless block_given?
@method_list.sort.each { |m| yield m }
end
##
# 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
def each_section # :yields: section, constants, attributes
return enum_for __method__ unless block_given?
constants = @constants.group_by do |constant| constant.section end
attributes = @attributes.group_by do |attribute| attribute.section end
constants.default = []
attributes.default = []
sort_sections.each do |section|
yield section, constants[section].select(&:display?).sort, attributes[section].select(&:display?).sort
end
end
##
# Finds an attribute +name+ with singleton value +singleton+.
def find_attribute(name, singleton)
name = $1 if name =~ /^(.*)=$/
@attributes.find { |a| a.name == name && a.singleton == singleton }
end
##
# Finds an attribute with +name+ in this context
def find_attribute_named(name)
case name
when /\A#/ then
find_attribute name[1..-1], false
when /\A::/ then
find_attribute name[2..-1], true
else
@attributes.find { |a| a.name == name }
end
end
##
# Finds a class method with +name+ in this context
def find_class_method_named(name)
@method_list.find { |meth| meth.singleton && meth.name == name }
end
##
# Finds a constant with +name+ in this context
def find_constant_named(name)
@constants.find do |m|
m.name == name || m.full_name == name
end
end
##
# Find a module at a higher scope
def find_enclosing_module_named(name)
parent && parent.find_module_named(name)
end
##
# Finds an external alias +name+ with singleton value +singleton+.
def find_external_alias(name, singleton)
@external_aliases.find { |m| m.name == name && m.singleton == singleton }
end
##
# Finds an external alias with +name+ in this context
def find_external_alias_named(name)
case name
when /\A#/ then
find_external_alias name[1..-1], false
when /\A::/ then
find_external_alias name[2..-1], true
else
@external_aliases.find { |a| a.name == name }
end
end
##
# Finds a file with +name+ in this context
def find_file_named name
@store.find_file_named name
end
##
# Finds an instance method with +name+ in this context
def find_instance_method_named(name)
@method_list.find { |meth| !meth.singleton && meth.name == name }
end
##
# 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
find_file_named(symbol)
end
##
# Finds a method named +name+ with singleton value +singleton+.
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 instance or module method with +name+ in this context
def find_method_named(name)
case name
when /\A#/ then
find_method name[1..-1], false
when /\A::/ then
find_method name[2..-1], true
else
@method_list.find { |meth| meth.name == name }
end
end
##
# Find a module with +name+ using ruby's scoping rules
def find_module_named(name)
res = @modules[name] || @classes[name]
return res if res
return self if self.name == name
find_enclosing_module_named name
end
##
# 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
##
# Look up a module named +symbol+.
def find_symbol_module(symbol)
result = nil
# look for a class or module 'symbol'
case symbol
when /^::/ then
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
end
end
result
end
##
# The full name for this context. This method is overridden by subclasses.
def full_name
'(unknown)'
end
##
# 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
##
# URL for this with a +prefix+
def http_url(prefix)
path = name_for_path
path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<
path = [prefix] + path.split('::')
File.join(*path.compact) + '.html'
end
##
# Instance attributes
def instance_attributes
@instance_attributes ||= attributes.reject { |a| a.singleton }
end
##
# Instance methods
def instance_methods
@instance_methods ||= method_list.reject { |a| a.singleton }
end
##
# Instance methods
#--
# TODO remove this later
def instance_method_list
warn '#instance_method_list is obsoleted, please use #instance_methods'
@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] = []
end
methods[type] = visibilities
end
each_method do |method|
next if section and not method.section == section
methods[method.type][method.visibility] << method
end
methods
end
##
# 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
each_ancestor do |parent|
parent.methods_matching(methods, singleton, &block)
end
end
##
# Array of modules in this context
def modules
@modules.values
end
##
# Hash of modules keyed by module name
def modules_hash
@modules
end
##
# Name to use to generate the url.
# #full_name by default.
def name_for_path
full_name
end
##
# Changes the visibility for new methods to +visibility+
def ongoing_visibility=(visibility)
@visibility = visibility
end
##
# Record +top_level+ as a file +self+ is in.
def record_location(top_level)
@in_files << top_level unless @in_files.include?(top_level)
end
##
# 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?
@remove_from_documentation ||=
@received_nodoc &&
!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
##
# 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
def remove_invisible_in array, min_visibility # :nodoc:
if min_visibility == :public then
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
##
# 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.
def section_contents
used_sections = {}
each_method do |method|
next unless method.display?
used_sections[method.section] = true
end
# order found sections
sections = sort_sections.select do |section|
used_sections[section]
end
# only the default section is used
return [] if
sections.length == 1 and not sections.first.title
sections
end
##
# Sections in this context
def sections
@sections.values
end
def sections_hash # :nodoc:
@sections
end
##
# Sets the current section to a section with +title+. See also #add_section
def set_current_section title, comment
@current_section = add_section title, comment
end
##
# Given an array +methods+ of method names, set the visibility of each to
# +visibility+
def set_visibility_for(methods, visibility, singleton = false)
methods_matching methods, singleton do |m|
m.visibility = visibility
end
end
##
# Given an array +names+ of constants, set the visibility of each constant to
# +visibility+
def set_constant_visibility_for(names, visibility)
names.each do |name|
constant = @constants_hash[name] or next
constant.visibility = visibility
end
end
##
# 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 } then
@sections.values_at(*TOMDOC_TITLES).compact
else
@sections.sort_by { |title, _|
title.to_s
}.map { |_, section|
section
}
end
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
##
# Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
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
# 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
autoload :Section, "#{__dir__}/context/section"
end
PK ! n " generator/pot/message_extractor.rbnu [ # frozen_string_literal: true
##
# Extracts message from RDoc::Store
class RDoc::Generator::POT::MessageExtractor
##
# Creates a message extractor for +store+.
def initialize store
@store = store
@po = RDoc::Generator::POT::PO.new
end
##
# 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
private
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_constant do |constant|
extract_text(constant.comment, constant.full_name)
end
klass.each_attribute do |attribute|
extract_text(attribute.comment, attribute.full_name)
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?
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
def entry msgid, options
RDoc::Generator::POT::POEntry.new(msgid, options)
end
end
PK ! jl{ generator/pot/po_entry.rbnu [ # 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 valus 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
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
PK ! generator/pot/po.rbnu [ # frozen_string_literal: true
##
# Generates a PO format text
class RDoc::Generator::POT::PO
##
# Creates an object that represents PO format.
def initialize
@entries = {}
add_header
end
##
# 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
##
# Returns PO format text for the PO.
def to_s
po = ''
sort_entries.each do |entry|
po += "\n" unless po.empty?
po += entry.to_s
end
po
end
private
def add_header
add(header_entry)
end
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