")
when ListBase::LABELED
annotate("") +
convert_flow(am.flow(fragment.param)) +
annotate("") +
annotate("")
when ListBase::NOTE
annotate("") +
annotate("| ") +
convert_flow(am.flow(fragment.param)) +
annotate(" | ") +
annotate("")
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
""
when ListBase::LABELED
""
when ListBase::NOTE
" |
"
else
raise "Invalid list type"
end
end
end
end
PK ! ) simple_markup/fragments.rbnu [ require 'rdoc/markup/simple_markup/lines.rb'
#require 'rdoc/markup/simple_markup/to_flow.rb'
module SM
##
# A Fragment is a chunk of text, subclassed as a paragraph, a list
# entry, or verbatim text
class Fragment
attr_reader :level, :param, :txt
attr_accessor :type
def initialize(level, param, type, txt)
@level = level
@param = param
@type = type
@txt = ""
add_text(txt) if txt
end
def add_text(txt)
@txt << " " if @txt.length > 0
@txt << txt.tr_s("\n ", " ").strip
end
def to_s
"L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
end
######
# This is a simple factory system that lets us associate fragement
# types (a string) with a subclass of fragment
TYPE_MAP = {}
def Fragment.type_name(name)
TYPE_MAP[name] = self
end
def Fragment.for(line)
klass = TYPE_MAP[line.type] ||
raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
return klass.new(line.level, line.param, line.flag, line.text)
end
end
##
# A paragraph is a fragment which gets wrapped to fit. We remove all
# newlines when we're created, and have them put back on output
class Paragraph < Fragment
type_name Line::PARAGRAPH
end
class BlankLine < Paragraph
type_name Line::BLANK
end
class Heading < Paragraph
type_name Line::HEADING
def head_level
@param.to_i
end
end
##
# A List is a fragment with some kind of label
#
class ListBase < Paragraph
# List types
BULLET = :BULLET
NUMBER = :NUMBER
UPPERALPHA = :UPPERALPHA
LOWERALPHA = :LOWERALPHA
LABELED = :LABELED
NOTE = :NOTE
end
class ListItem < ListBase
type_name Line::LIST
# def label
# am = AttributeManager.new(@param)
# am.flow
# end
end
class ListStart < ListBase
def initialize(level, param, type)
super(level, param, type, nil)
end
end
class ListEnd < ListBase
def initialize(level, type)
super(level, "", type, nil)
end
end
##
# Verbatim code contains lines that don't get wrapped.
class Verbatim < Fragment
type_name Line::VERBATIM
def add_text(txt)
@txt << txt.chomp << "\n"
end
end
##
# A horizontal rule
class Rule < Fragment
type_name Line::RULE
end
# Collect groups of lines together. Each group
# will end up containing a flow of text
class LineCollection
def initialize
@fragments = []
end
def add(fragment)
@fragments << fragment
end
def each(&b)
@fragments.each(&b)
end
# For testing
def to_a
@fragments.map {|fragment| fragment.to_s}
end
# Factory for different fragment types
def fragment_for(*args)
Fragment.for(*args)
end
# tidy up at the end
def normalize
change_verbatim_blank_lines
add_list_start_and_ends
add_list_breaks
tidy_blank_lines
end
def to_s
@fragments.join("\n----\n")
end
def accept(am, visitor)
visitor.start_accepting
@fragments.each do |fragment|
case fragment
when Verbatim
visitor.accept_verbatim(am, fragment)
when Rule
visitor.accept_rule(am, fragment)
when ListStart
visitor.accept_list_start(am, fragment)
when ListEnd
visitor.accept_list_end(am, fragment)
when ListItem
visitor.accept_list_item(am, fragment)
when BlankLine
visitor.accept_blank_line(am, fragment)
when Heading
visitor.accept_heading(am, fragment)
when Paragraph
visitor.accept_paragraph(am, fragment)
end
end
visitor.end_accepting
end
#######
private
#######
# If you have:
#
# normal paragraph text.
#
# this is code
#
# and more code
#
# You'll end up with the fragments Paragraph, BlankLine,
# Verbatim, BlankLine, Verbatim, BlankLine, etc
#
# The BlankLine in the middle of the verbatim chunk needs to
# be changed to a real verbatim newline, and the two
# verbatim blocks merged
#
#
def change_verbatim_blank_lines
frag_block = nil
blank_count = 0
@fragments.each_with_index do |frag, i|
if frag_block.nil?
frag_block = frag if Verbatim === frag
else
case frag
when Verbatim
blank_count.times { frag_block.add_text("\n") }
blank_count = 0
frag_block.add_text(frag.txt)
@fragments[i] = nil # remove out current fragment
when BlankLine
if frag_block
blank_count += 1
@fragments[i] = nil
end
else
frag_block = nil
blank_count = 0
end
end
end
@fragments.compact!
end
# List nesting is implicit given the level of
# Make it explicit, just to make life a tad
# easier for the output processors
def add_list_start_and_ends
level = 0
res = []
type_stack = []
@fragments.each do |fragment|
# $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
new_level = fragment.level
while (level < new_level)
level += 1
type = fragment.type
res << ListStart.new(level, fragment.param, type) if type
type_stack.push type
# $stderr.puts "Start: #{level}"
end
while level > new_level
type = type_stack.pop
res << ListEnd.new(level, type) if type
level -= 1
# $stderr.puts "End: #{level}, #{type}"
end
res << fragment
level = fragment.level
end
level.downto(1) do |i|
type = type_stack.pop
res << ListEnd.new(i, type) if type
end
@fragments = res
end
# now insert start/ends between list entries at the
# same level that have different element types
def add_list_breaks
res = @fragments
@fragments = []
list_stack = []
res.each do |fragment|
case fragment
when ListStart
list_stack.push fragment
when ListEnd
start = list_stack.pop
fragment.type = start.type
when ListItem
l = list_stack.last
if fragment.type != l.type
@fragments << ListEnd.new(l.level, l.type)
start = ListStart.new(l.level, fragment.param, fragment.type)
@fragments << start
list_stack.pop
list_stack.push start
end
else
;
end
@fragments << fragment
end
end
# Finally tidy up the blank lines:
# * change Blank/ListEnd into ListEnd/Blank
# * remove blank lines at the front
def tidy_blank_lines
(@fragments.size - 1).times do |i|
if @fragments[i].kind_of?(BlankLine) and
@fragments[i+1].kind_of?(ListEnd)
@fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
end
end
# remove leading blanks
@fragments.each_with_index do |f, i|
break unless f.kind_of? BlankLine
@fragments[i] = nil
end
@fragments.compact!
end
end
end
PK ! Vڹ simple_markup/preprocess.rbnu [ module SM
##
# Handle common directives that can occur in a block of text:
#
# : include : filename
#
class PreProcess
def initialize(input_file_name, include_path)
@input_file_name = input_file_name
@include_path = include_path
end
# Look for common options in a chunk of text. Options that
# we don't handle are passed back to our caller
# as |directive, param|
def handle(text)
text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
prefix = $1
directive = $2.downcase
param = $3
case directive
when "include"
filename = param.split[0]
include_file(filename, prefix)
else
yield(directive, param)
end
end
end
#######
private
#######
# Include a file, indenting it correctly
def include_file(name, indent)
if (full_name = find_include_file(name))
content = File.open(full_name) {|f| f.read}
# strip leading '#'s, but only if all lines start with them
if content =~ /^[^#]/
content.gsub(/^/, indent)
else
content.gsub(/^#?/, indent)
end
else
$stderr.puts "Couldn't find file to include: '#{name}'"
''
end
end
# Look for the given file in the directory containing the current
# file, and then in each of the directories specified in the
# RDOC_INCLUDE path
def find_include_file(name)
to_search = [ File.dirname(@input_file_name) ].concat @include_path
to_search.each do |dir|
full_name = File.join(dir, name)
stat = File.stat(full_name) rescue next
return full_name if stat.readable?
end
nil
end
end
end
PK ! 7^! ! simple_markup/to_latex.rbnu [ require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/inline'
require 'cgi'
module SM
# Convert SimpleMarkup to basic LaTeX report format
class ToLaTeX
BS = "\020" # \
OB = "\021" # {
CB = "\022" # }
DL = "\023" # Dollar
BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
HAT = "#{BS}symbol#{OB}94#{CB}"
BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
TILDE = "#{DL}#{BS}sim#{DL}"
LESSTHAN = "#{DL}<#{DL}"
GREATERTHAN = "#{DL}>#{DL}"
def self.l(str)
str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
end
def l(arg)
SM::ToLaTeX.l(arg)
end
LIST_TYPE_TO_LATEX = {
ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
ListBase::NOTE => [
l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
l("\\end{tabularx}") ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
@list_depth = 0
@prev_list_types = []
end
##
# Set up the standard mapping of attributes to LaTeX
#
def init_tags
@attr_tags = [
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
]
end
##
# Escape a LaTeX string
def escape(str)
# $stderr.print "FE: ", str
s = str.
# sub(/\s+$/, '').
gsub(/([_\${}&%#])/, "#{BS}\\1").
gsub(/\\/, BACKSLASH).
gsub(/\^/, HAT).
gsub(/~/, TILDE).
gsub(/, LESSTHAN).
gsub(/>/, GREATERTHAN).
gsub(/,,/, ",{},").
gsub(/\`/, BACKQUOTE)
# $stderr.print "-> ", s, "\n"
s
end
##
# Add a new set of LaTeX tags for an attribute. We allow
# separate start and end tags for flexibility
#
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
end
def accept_paragraph(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << "\n"
end
def accept_verbatim(am, fragment)
@res << "\n\\begin{code}\n"
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
@res << "\n\\end{code}\n\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
end
def accept_list_start(am, fragment)
@res << list_name(fragment.type, true) <<"\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << tag << "\n"
end
@res << list_name(fragment.type, false) <<"\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << tag << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
# This is a higher speed (if messier) version of wrap
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
end
#######################################################################
private
#######################################################################
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << tag.on
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << tag.off
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
# $stderr.puts "Converting '#{item}'"
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
# some of these patterns are taken from SmartyPants...
def convert_string(item)
escape(item).
# convert ... to elipsis (and make sure .... becomes .)
gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
gsub(%r{\'(?=\W|s\b)}) { "'" }.
# convert single opening quote
gsub(/'/, '`').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
# convert double opening quote
gsub(/"/, "``").
# convert copyright
gsub(/\(c\)/, '\copyright{}')
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
case level
when 1 then "\\chapter{"
when 2 then "\\section{"
when 3 then "\\subsection{"
when 4 then "\\subsubsection{"
else "\\paragraph{"
end +
convert_flow(flow) +
"}\n"
end
def list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
if tags[2] # enumerate
if is_open_tag
@list_depth += 1
if @prev_list_types[@list_depth] != tags[2]
case @list_depth
when 1
roman = "i"
when 2
roman = "ii"
when 3
roman = "iii"
when 4
roman = "iv"
else
raise("Too deep list: level #{@list_depth}")
end
@prev_list_types[@list_depth] = tags[2]
return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
end
else
@list_depth -= 1
end
end
tags[ is_open_tag ? 0 : 1]
end
def list_item_start(am, fragment)
case fragment.type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
"\\item "
when ListBase::LABELED
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
when ListBase::NOTE
convert_flow(am.flow(fragment.param)) + " & "
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA, ListBase::LABELED
""
when ListBase::NOTE
"\\\\\n"
else
raise "Invalid list type"
end
end
end
end
PK ! " " simple_markup/inline.rbnu [ module SM
# We manage a set of attributes. Each attribute has a symbol name
# and a bit value
class Attribute
SPECIAL = 1
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
@@next_bitmap = 2
def Attribute.bitmap_for(name)
bitmap = @@name_to_bitmap[name]
if !bitmap
bitmap = @@next_bitmap
@@next_bitmap <<= 1
@@name_to_bitmap[name] = bitmap
end
bitmap
end
def Attribute.as_string(bitmap)
return "none" if bitmap.zero?
res = []
@@name_to_bitmap.each do |name, bit|
res << name if (bitmap & bit) != 0
end
res.join(",")
end
def Attribute.each_name_of(bitmap)
@@name_to_bitmap.each do |name, bit|
next if bit == SPECIAL
yield name.to_s if (bitmap & bit) != 0
end
end
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
AttrChanger = Struct.new(:turn_on, :turn_off)
class AttrChanger
def to_s
"Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
end
end
# An array of attributes which parallels the characters in a string
class AttrSpan
def initialize(length)
@attrs = Array.new(length, 0)
end
def set_attrs(start, length, bits)
for i in start ... (start+length)
@attrs[i] |= bits
end
end
def [](n)
@attrs[n]
end
end
##
# Hold details of a special sequence
class Special
attr_reader :type
attr_accessor :text
def initialize(type, text)
@type, @text = type, text
end
def ==(o)
self.text == o.text && self.type == o.type
end
def to_s
"Special: type=#{type}, text=#{text.dump}"
end
end
class AttributeManager
NULL = "\000".freeze
##
# We work by substituting non-printing characters in to the
# text. For now I'm assuming that I can substitute
# a character in the range 0..8 for a 7 bit character
# without damaging the encoded string, but this might
# be optimistic
#
A_PROTECT = 004
PROTECT_ATTR = A_PROTECT.chr
# This maps delimiters that occur around words (such as
# *bold* or +tt+) where the start and end delimiters
# and the same. This lets us optimize the regexp
MATCHING_WORD_PAIRS = {}
# And this is used when the delimiters aren't the same. In this
# case the hash maps a pattern to the attribute character
WORD_PAIR_MAP = {}
# This maps HTML tags to the corresponding attribute char
HTML_TAGS = {}
# And this maps _special_ sequences to a name. A special sequence
# is something like a WikiWord
SPECIAL = {}
# Return an attribute object with the given turn_on
# and turn_off bits set
def attribute(turn_on, turn_off)
AttrChanger.new(turn_on, turn_off)
end
def change_attribute(current, new)
diff = current ^ new
attribute(new & diff, current & diff)
end
def changed_attribute_by_name(current_set, new_set)
current = new = 0
current_set.each {|name| current |= Attribute.bitmap_for(name) }
new_set.each {|name| new |= Attribute.bitmap_for(name) }
change_attribute(current, new)
end
def copy_string(start_pos, end_pos)
res = @str[start_pos...end_pos]
res.gsub!(/\000/, '')
res
end
# Map attributes like textto the sequence \001\002\001\003,
# where is a per-attribute specific character
def convert_attrs(str, attrs)
# first do matching ones
tags = MATCHING_WORD_PAIRS.keys.join("")
re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
1 while str.gsub!(Regexp.new(re)) {
attr = MATCHING_WORD_PAIRS[$2];
attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
$1 + NULL*$2.length + $3 + NULL*$2.length + $4
}
# then non-matching
unless WORD_PAIR_MAP.empty?
WORD_PAIR_MAP.each do |regexp, attr|
str.gsub!(regexp) {
attrs.set_attrs($`.length + $1.length, $2.length, attr)
NULL*$1.length + $2 + NULL*$3.length
}
end
end
end
def convert_html(str, attrs)
tags = HTML_TAGS.keys.join("|")
re = "<(#{tags})>(.*?)\\1>"
1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
attr = HTML_TAGS[$1.downcase]
html_length = $1.length + 2
seq = NULL * html_length
attrs.set_attrs($`.length + html_length, $2.length, attr)
seq + $2 + seq + NULL
}
end
def convert_specials(str, attrs)
unless SPECIAL.empty?
SPECIAL.each do |regexp, attr|
str.scan(regexp) do
attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
end
end
end
end
# A \ in front of a character that would normally be
# processed turns off processing. We do this by turning
# \< into <#{PROTECT}
PROTECTABLE = [ "<" << "\\" ] #"
def mask_protected_sequences
protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
@str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
end
def unmask_protected_sequences
@str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
end
def initialize
add_word_pair("*", "*", :BOLD)
add_word_pair("_", "_", :EM)
add_word_pair("+", "+", :TT)
add_html("em", :EM)
add_html("i", :EM)
add_html("b", :BOLD)
add_html("tt", :TT)
add_html("code", :TT)
add_special(//, :COMMENT)
end
def add_word_pair(start, stop, name)
raise "Word flags may not start '<'" if start[0] == ?<
bitmap = Attribute.bitmap_for(name)
if start == stop
MATCHING_WORD_PAIRS[start] = bitmap
else
pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
# "([A-Za-z]+)" +
"(\\S+)" +
"(" + Regexp.escape(stop) +")")
WORD_PAIR_MAP[pattern] = bitmap
end
PROTECTABLE << start[0,1]
PROTECTABLE.uniq!
end
def add_html(tag, name)
HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
end
def add_special(pattern, name)
SPECIAL[pattern] = Attribute.bitmap_for(name)
end
def flow(str)
@str = str
puts("Before flow, str='#{@str.dump}'") if $DEBUG
mask_protected_sequences
@attrs = AttrSpan.new(@str.length)
puts("After protecting, str='#{@str.dump}'") if $DEBUG
convert_attrs(@str, @attrs)
convert_html(@str, @attrs)
convert_specials(str, @attrs)
unmask_protected_sequences
puts("After flow, str='#{@str.dump}'") if $DEBUG
return split_into_flow
end
def display_attributes
puts
puts @str.tr(NULL, "!")
bit = 1
16.times do |bno|
line = ""
@str.length.times do |i|
if (@attrs[i] & bit) == 0
line << " "
else
if bno.zero?
line << "S"
else
line << ("%d" % (bno+1))
end
end
end
puts(line) unless line =~ /^ *$/
bit <<= 1
end
end
def split_into_flow
display_attributes if $DEBUG
res = []
current_attr = 0
str = ""
str_len = @str.length
# skip leading invisible text
i = 0
i += 1 while i < str_len and @str[i].zero?
start_pos = i
# then scan the string, chunking it on attribute changes
while i < str_len
new_attr = @attrs[i]
if new_attr != current_attr
if i > start_pos
res << copy_string(start_pos, i)
start_pos = i
end
res << change_attribute(current_attr, new_attr)
current_attr = new_attr
if (current_attr & Attribute::SPECIAL) != 0
i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
res << Special.new(current_attr, copy_string(start_pos, i))
start_pos = i
next
end
end
# move on, skipping any invisible characters
begin
i += 1
end while i < str_len and @str[i].zero?
end
# tidy up trailing text
if start_pos < str_len
res << copy_string(start_pos, str_len)
end
# and reset to all attributes off
res << change_attribute(current_attr, 0) if current_attr != 0
return res
end
end
end
PK ! k simple_markup/lines.rbnu [ ##########################################################################
#
# We store the lines we're working on as objects of class Line.
# These contain the text of the line, along with a flag indicating the
# line type, and an indentation level
module SM
class Line
INFINITY = 9999
BLANK = :BLANK
HEADING = :HEADING
LIST = :LIST
RULE = :RULE
PARAGRAPH = :PARAGRAPH
VERBATIM = :VERBATIM
# line type
attr_accessor :type
# The indentation nesting level
attr_accessor :level
# The contents
attr_accessor :text
# A prefix or parameter. For LIST lines, this is
# the text that introduced the list item (the label)
attr_accessor :param
# A flag. For list lines, this is the type of the list
attr_accessor :flag
# the number of leading spaces
attr_accessor :leading_spaces
# true if this line has been deleted from the list of lines
attr_accessor :deleted
def initialize(text)
@text = text.dup
@deleted = false
# expand tabs
1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
# Strip trailing whitespace
@text.sub!(/\s+$/, '')
# and look for leading whitespace
if @text.length > 0
@text =~ /^(\s*)/
@leading_spaces = $1.length
else
@leading_spaces = INFINITY
end
end
# Return true if this line is blank
def isBlank?
@text.length.zero?
end
# stamp a line with a type, a level, a prefix, and a flag
def stamp(type, level, param="", flag=nil)
@type, @level, @param, @flag = type, level, param, flag
end
##
# Strip off the leading margin
#
def strip_leading(size)
if @text.size > size
@text[0,size] = ""
else
@text = ""
end
end
def to_s
"#@type#@level: #@text"
end
end
###############################################################################
#
# A container for all the lines
#
class Lines
include Enumerable
attr_reader :lines # for debugging
def initialize(lines)
@lines = lines
rewind
end
def empty?
@lines.size.zero?
end
def each
@lines.each do |line|
yield line unless line.deleted
end
end
# def [](index)
# @lines[index]
# end
def rewind
@nextline = 0
end
def next
begin
res = @lines[@nextline]
@nextline += 1 if @nextline < @lines.size
end while res and res.deleted and @nextline < @lines.size
res
end
def unget
@nextline -= 1
end
def delete(a_line)
a_line.deleted = true
end
def normalize
margin = @lines.collect{|l| l.leading_spaces}.min
margin = 0 if margin == Line::INFINITY
@lines.each {|line| line.strip_leading(margin) } if margin > 0
end
def as_text
@lines.map {|l| l.text}.join("\n")
end
def line_types
@lines.map {|l| l.type }
end
end
end
PK ! 8a U~ ~ simple_markup/to_flow.rbnu [ require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/inline'
require 'cgi'
module SM
module Flow
P = Struct.new(:body)
VERB = Struct.new(:body)
RULE = Struct.new(:width)
class LIST
attr_reader :type, :contents
def initialize(type)
@type = type
@contents = []
end
def <<(stuff)
@contents << stuff
end
end
LI = Struct.new(:label, :body)
H = Struct.new(:level, :text)
end
class ToFlow
LIST_TYPE_TO_HTML = {
SM::ListBase::BULLET => [ "" ],
SM::ListBase::NUMBER => [ "", "
" ],
SM::ListBase::UPPERALPHA => [ "", "
" ],
SM::ListBase::LOWERALPHA => [ "", "
" ],
SM::ListBase::LABELED => [ "", "
" ],
SM::ListBase::NOTE => [ "" ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
end
##
# Set up the standard mapping of attributes to HTML tags
#
def init_tags
@attr_tags = [
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "", ""),
InlineTag.new(SM::Attribute.bitmap_for(:TT), "", ""),
InlineTag.new(SM::Attribute.bitmap_for(:EM), "", ""),
]
end
##
# Add a new set of HTML tags for an attribute. We allow
# separate start and end tags for flexibility
#
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
end
##
# Given an HTML tag, decorate it with class information
# and the like if required. This is a no-op in the base
# class, but is overridden in HTML output classes that
# implement style sheets
def annotate(tag)
tag
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = []
@list_stack = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
end
def accept_verbatim(am, fragment)
@res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << Flow::RULE.new(size)
end
def accept_list_start(am, fragment)
@list_stack.push(@res)
list = Flow::LIST.new(fragment.type)
@res << list
@res = list
end
def accept_list_end(am, fragment)
@res = @list_stack.pop
end
def accept_list_item(am, fragment)
@res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
end
def accept_blank_line(am, fragment)
# @res << annotate("") << "\n"
end
def accept_heading(am, fragment)
@res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
end
#######################################################################
private
#######################################################################
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.on)
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.off)
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
# some of these patterns are taken from SmartyPants...
def convert_string(item)
CGI.escapeHTML(item)
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
end
end
PK ! /
heading.rbnu [ ##
# A heading with a level (1-6) and text
RDoc::Markup::Heading =
Struct.new :level, :text do
@to_html = nil
@to_label = nil
##
# A singleton RDoc::Markup::ToLabel formatter for headings.
def self.to_label
@to_label ||= RDoc::Markup::ToLabel.new
end
##
# A singleton plain HTML formatter for headings. Used for creating labels
# for the Table of Contents
def self.to_html
return @to_html if @to_html
markup = RDoc::Markup.new
markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
@to_html = RDoc::Markup::ToHtml.new nil
def @to_html.handle_special_CROSSREF special
special.text.sub(/^\\/, '')
end
@to_html
end
##
# Calls #accept_heading on +visitor+
def accept visitor
visitor.accept_heading self
end
##
# An HTML-safe anchor reference for this header.
def aref
"label-#{self.class.to_label.convert text.dup}"
end
##
# Creates a fully-qualified label which will include the label from
# +context+. This helps keep ids unique in HTML.
def label context = nil
label = aref
label = [context.aref, label].compact.join '-' if
context and context.respond_to? :aref
label
end
##
# HTML markup of the text of this label without the surrounding header
# element.
def plain_html
self.class.to_html.to_html(text.dup)
end
def pretty_print q # :nodoc:
q.group 2, "[head: #{level} ", ']' do
q.pp text
end
end
end
PK ! fР
attributes.rbnu [ ##
# We manage a set of attributes. Each attribute has a symbol name and a bit
# value.
class RDoc::Markup::Attributes
##
# The special attribute type. See RDoc::Markup#add_special
attr_reader :special
##
# Creates a new attributes set.
def initialize
@special = 1
@name_to_bitmap = [
[:_SPECIAL_, @special],
]
@next_bitmap = @special << 1
end
##
# Returns a unique bit for +name+
def bitmap_for name
bitmap = @name_to_bitmap.assoc name
unless bitmap then
bitmap = @next_bitmap
@next_bitmap <<= 1
@name_to_bitmap << [name, bitmap]
else
bitmap = bitmap.last
end
bitmap
end
##
# Returns a string representation of +bitmap+
def as_string bitmap
return 'none' if bitmap.zero?
res = []
@name_to_bitmap.each do |name, bit|
res << name if (bitmap & bit) != 0
end
res.join ','
end
##
# yields each attribute name in +bitmap+
def each_name_of bitmap
return enum_for __method__, bitmap unless block_given?
@name_to_bitmap.each do |name, bit|
next if bit == @special
yield name.to_s if (bitmap & bit) != 0
end
end
end
PK ! '>' ' list.rbnu [ ##
# A List is a homogeneous set of ListItems.
#
# The supported list types include:
#
# :BULLET::
# An unordered list
# :LABEL::
# An unordered definition list, but using an alternate RDoc::Markup syntax
# :LALPHA::
# An ordered list using increasing lowercase English letters
# :NOTE::
# An unordered definition list
# :NUMBER::
# An ordered list using increasing Arabic numerals
# :UALPHA::
# An ordered list using increasing uppercase English letters
#
# Definition lists behave like HTML definition lists. Each list item can
# describe multiple terms. See RDoc::Markup::ListItem for how labels and
# definition are stored as list items.
class RDoc::Markup::List
##
# The list's type
attr_accessor :type
##
# Items in the list
attr_reader :items
##
# Creates a new list of +type+ with +items+. Valid list types are:
# +:BULLET+, +:LABEL+, +:LALPHA+, +:NOTE+, +:NUMBER+, +:UALPHA+
def initialize type = nil, *items
@type = type
@items = []
@items.concat items
end
##
# Appends +item+ to the list
def << item
@items << item
end
def == other # :nodoc:
self.class == other.class and
@type == other.type and
@items == other.items
end
##
# Runs this list and all its #items through +visitor+
def accept visitor
visitor.accept_list_start self
@items.each do |item|
item.accept visitor
end
visitor.accept_list_end self
end
##
# Is the list empty?
def empty?
@items.empty?
end
##
# Returns the last item in the list
def last
@items.last
end
def pretty_print q # :nodoc:
q.group 2, "[list: #{@type} ", ']' do
q.seplist @items do |item|
q.pp item
end
end
end
##
# Appends +items+ to the list
def push *items
@items.concat items
end
end
PK ! H2 2 to_joined_paragraph.rbnu [ ##
# Joins the parts of an RDoc::Markup::Paragraph into a single String.
#
# This allows for easier maintenance and testing of Markdown support.
#
# This formatter only works on Paragraph instances. Attempting to process
# other markup syntax items will not work.
class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
def initialize # :nodoc:
super nil
end
def start_accepting # :nodoc:
end
def end_accepting # :nodoc:
end
##
# Converts the parts of +paragraph+ to a single entry.
def accept_paragraph paragraph
parts = []
string = false
paragraph.parts.each do |part|
if String === part then
if string then
string << part
else
parts << part
string = part
end
else
parts << part
string = false
end
end
parts = parts.map do |part|
if String === part then
part.rstrip
else
part
end
end
# TODO use Enumerable#chunk when Ruby 1.8 support is dropped
#parts = paragraph.parts.chunk do |part|
# String === part
#end.map do |string, chunk|
# string ? chunk.join.rstrip : chunk
#end.flatten
paragraph.parts.replace parts
end
alias accept_block_quote ignore
alias accept_heading ignore
alias accept_list_end ignore
alias accept_list_item_end ignore
alias accept_list_item_start ignore
alias accept_list_start ignore
alias accept_raw ignore
alias accept_rule ignore
alias accept_verbatim ignore
end
PK ! w! !
to_html.rbnu [ require 'cgi'
##
# Outputs RDoc markup as HTML.
class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
include RDoc::Text
# :section: Utilities
##
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
LIST_TYPE_TO_HTML = {
:BULLET => [''],
:LABEL => ['', '
'],
:LALPHA => ['', '
'],
:NOTE => ['', '
'],
:NUMBER => ['', '
'],
:UALPHA => ['', '
'],
}
attr_reader :res # :nodoc:
attr_reader :in_list_entry # :nodoc:
attr_reader :list # :nodoc:
##
# The RDoc::CodeObject HTML is being generated for. This is used to
# generate namespaced URI fragments
attr_accessor :code_object
##
# Path to this document for relative links
attr_accessor :from_path
# :section:
##
# Creates a new formatter that will output HTML
def initialize options, markup = nil
super
@code_object = nil
@from_path = ''
@in_list_entry = nil
@list = nil
@th = nil
@hard_break = "
\n"
# external links
@markup.add_special(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/,
:HYPERLINK)
add_special_RDOCLINK
add_special_TIDYLINK
init_tags
end
# :section: Special Handling
#
# These methods handle special markup added by RDoc::Markup#add_special.
def handle_RDOCLINK url # :nodoc:
case url
when /^rdoc-ref:/
$'
when /^rdoc-label:/
text = $'
text = case text
when /\Alabel-/ then $'
when /\Afootmark-/ then $'
when /\Afoottext-/ then $'
else text
end
gen_url url, text
when /^rdoc-image:/
"
"
else
url =~ /\Ardoc-[a-z]+:/
$'
end
end
##
# +special+ is a
def handle_special_HARD_BREAK special
'
'
end
##
# +special+ is a potential link. The following schemes are handled:
#
# mailto:::
# Inserted as-is.
# http:::
# Links are checked to see if they reference an image. If so, that image
# gets inserted using an
tag. Otherwise a conventional
# is used.
# link:::
# Reference to a local file relative to the output directory.
def handle_special_HYPERLINK(special)
url = special.text
gen_url url, url
end
##
# +special+ is an rdoc-schemed link that will be converted into a hyperlink.
#
# For the +rdoc-ref+ scheme the named reference will be returned without
# creating a link.
#
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
# when creating a link. All other contents will be linked verbatim.
def handle_special_RDOCLINK special
handle_RDOCLINK special.text
end
##
# This +special+ is a link where the label is different from the URL
# label[url] or {long label}[url]
def handle_special_TIDYLINK(special)
text = special.text
return text unless
text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
label = $1
url = $2
label = handle_RDOCLINK label if /^rdoc-image:/ =~ label
gen_url url, label
end
# :section: Visitor
#
# These methods implement the HTML visitor.
##
# Prepares the visitor for HTML generation
def start_accepting
@res = []
@in_list_entry = []
@list = []
end
##
# Returns the generated output
def end_accepting
@res.join
end
##
# Adds +block_quote+ to the output
def accept_block_quote block_quote
@res << "\n"
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 << wrap(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::RubyLex.tokenize text, @options
klass = ' class="ruby"'
RDoc::TokenStream.to_html tokens
rescue RDoc::RubyLex::Error
CGI.escapeHTML text
end
else
CGI.escapeHTML text
end
if @options.pipe then
@res << "\n#{CGI.escapeHTML text}
\n"
else
@res << "\n#{content}\n"
end
end
##
# Adds +rule+ to the output
def accept_rule rule
@res << "
\n"
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 false
end
##
# Finishes consumption of +list+
def accept_list_end(list)
@list.pop
if tag = @in_list_entry.pop
@res << tag
end
@res << html_list_name(list.type, false) << "\n"
end
##
# Prepares the visitor for consuming +list_item+
def accept_list_item_start(list_item)
if tag = @in_list_entry.last
@res << tag
end
@res << list_item_start(list_item, @list.last)
end
##
# Finishes consumption of +list_item+
def accept_list_item_end(list_item)
@in_list_entry[-1] = list_end_for(@list.last)
end
##
# Adds +blank_line+ to the output
def accept_blank_line(blank_line)
# @res << annotate("") << "\n"
end
##
# Adds +heading+ to the output. The headings greater than 6 are trimmed to
# level 6.
def accept_heading heading
level = [6, heading.level].min
label = heading.label @code_object
@res << if @options.output_decoration
"\n"
else
"\n"
end
@res << to_html(heading.text)
unless @options.pipe then
@res << "¶"
@res << " ↑"
end
@res << "\n"
end
##
# Adds +raw+ to the output
def accept_raw raw
@res << raw.parts.join("\n")
end
# :section: Utilities
##
# CGI-escapes +text+
def convert_string(text)
CGI.escapeHTML text
end
##
# Generate a link to +url+ with content +text+. Handles the special cases
# for img: and link: described under handle_special_HYPERLINK
def gen_url url, text
scheme, url, id = parse_url url
if %w[http https link].include?(scheme) and
url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
"
"
else
text = text.sub %r%^#{scheme}:/*%i, ''
text = text.sub %r%^[*\^](\d+)$%, '\1'
link = "#{text}"
link = "#{link}" if /"foot/ =~ id
link
end
end
##
# Determines the HTML list element for +list_type+ and +open_tag+
def html_list_name(list_type, open_tag)
tags = LIST_TYPE_TO_HTML[list_type]
raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
tags[open_tag ? 0 : 1]
end
##
# Maps attributes to HTML tags
def init_tags
add_tag :BOLD, "", ""
add_tag :TT, "", ""
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
""
when :LABEL, :NOTE then
Array(list_item.label).map do |label|
"#{to_html label}\n"
end.join << ""
else
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
##
# Returns the HTML end-tag for +list_type+
def list_end_for(list_type)
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
""
when :LABEL, :NOTE then
""
else
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
##
# Returns true if Ripper is available it can create a sexp from +text+
def parseable? text
text =~ /\b(def|class|module|require) |=>|\{\s?\||do \|/ and
text !~ /<%|%>/
end
##
# Converts +item+ to HTML using RDoc::Text#to_html
def to_html item
super convert_flow @am.flow item
end
end
PK ! $AEb to_bs.rbnu [ ##
# Outputs RDoc markup with hot backspace action! You will probably need a
# pager to use this output format.
#
# This formatter won't work on 1.8.6 because it lacks String#chars.
class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
##
# Returns a new ToBs that is ready for hot backspace action!
def initialize markup = nil
super
@in_b = false
@in_em = false
end
##
# Sets a flag that is picked up by #annotate to do the right thing in
# #convert_string
def init_tags
add_tag :BOLD, '+b', '-b'
add_tag :EM, '+_', '-_'
add_tag :TT, '' , '' # we need in_tt information maintained
end
##
# Makes heading text bold.
def accept_heading heading
use_prefix or @res << ' ' * @indent
@res << @headings[heading.level][0]
@in_b = true
@res << attributes(heading.text)
@in_b = false
@res << @headings[heading.level][1]
@res << "\n"
end
##
# Turns on or off special handling for +convert_string+
def annotate tag
case tag
when '+b' then @in_b = true
when '-b' then @in_b = false
when '+_' then @in_em = true
when '-_' then @in_em = false
end
''
end
##
# Calls convert_string on the result of convert_special
def convert_special special
convert_string super
end
##
# Adds bold or underline mixed with backspaces
def convert_string string
return string unless string.respond_to? :chars # your ruby is lame
return string unless @in_b or @in_em
chars = if @in_b then
string.chars.map do |char| "#{char}\b#{char}" end
elsif @in_em then
string.chars.map do |char| "_\b#{char}" end
end
chars.join
end
end
PK ! Up p
to_test.rbnu [ ##
# This Markup outputter is used for testing purposes.
class RDoc::Markup::ToTest < RDoc::Markup::Formatter
# :stopdoc:
##
# :section: Visitor
def start_accepting
@res = []
@list = []
end
def end_accepting
@res
end
def accept_paragraph(paragraph)
@res << convert_flow(@am.flow(paragraph.text))
end
def accept_raw raw
@res << raw.parts.join
end
def accept_verbatim(verbatim)
@res << verbatim.text.gsub(/^(\S)/, ' \1')
end
def accept_list_start(list)
@list << case list.type
when :BULLET then
'*'
when :NUMBER then
'1'
else
list.type
end
end
def accept_list_end(list)
@list.pop
end
def accept_list_item_start(list_item)
@res << "#{' ' * (@list.size - 1)}#{@list.last}: "
end
def accept_list_item_end(list_item)
end
def accept_blank_line(blank_line)
@res << "\n"
end
def accept_heading(heading)
@res << "#{'=' * heading.level} #{heading.text}"
end
def accept_rule(rule)
@res << '-' * rule.weight
end
# :startdoc:
end
PK ! ?| | table.rbnu [ # frozen_string_literal: true
##
# A section of table
class RDoc::Markup::Table
# headers of each column
attr_accessor :header
# alignments of each column
attr_accessor :align
# body texts of each column
attr_accessor :body
# Creates new instance
def initialize(header, align, body)
@header, @align, @body = header, align, body
end
# :stopdoc:
def ==(other)
self.class == other.class and
@header == other.header and
@align == other.align and
@body == other.body
end
def accept(visitor)
visitor.accept_table @header, @body, @align
end
def pretty_print(q)
q.group 2, '[Table: ', ']' do
q.group 2, '[Head: ', ']' do
q.seplist @header.zip(@align) do |text, align|
q.pp text
if align
q.text ":"
q.breakable
q.text align.to_s
end
end
end
q.breakable
q.group 2, '[Body: ', ']' do
q.seplist @body do |body|
q.group 2, '[', ']' do
q.seplist body do |text|
q.pp text
end
end
end
end
end
end
end
PK ! X! to_html_snippet.rbnu [ ##
# Outputs RDoc markup as paragraphs with inline markup only.
class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
##
# After this many characters the input will be cut off.
attr_reader :character_limit
##
# The number of characters seen so far.
attr_reader :characters # :nodoc:
##
# The attribute bitmask
attr_reader :mask
##
# After this many paragraphs the input will be cut off.
attr_reader :paragraph_limit
##
# Count of paragraphs found
attr_reader :paragraphs
##
# Creates a new ToHtmlSnippet formatter that will cut off the input on the
# next word boundary after the given number of +characters+ or +paragraphs+
# of text have been encountered.
def initialize options, characters = 100, paragraphs = 3, markup = nil
super options, markup
@character_limit = characters
@paragraph_limit = paragraphs
@characters = 0
@mask = 0
@paragraphs = 0
@markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
end
##
# Adds +heading+ to the output as a paragraph
def accept_heading heading
@res << "#{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}#{wrap 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 +special+
def handle_special_CROSSREF special
special.text.sub(/\A\\/, '')
end
##
# +special+ is a
def handle_special_HARD_BREAK special
@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::Special then
text = convert_special 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 ! :|\l l indented_paragraph.rbnu [ ##
# 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 ! ,\ \ to_markdown.rbnu [ # :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_special_RDOCLINK
add_special_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_special_HARD_BREAK special
" \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" unless @res =~ /\n\z/
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_special_TIDYLINK special
text = special.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_special_RDOCLINK special
handle_rdoc_link special.text
end
end
PK ! 3ɀ
to_ansi.rbnu [ ##
# 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 ! U to_label.rbnu [ require 'cgi'
##
# 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_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
@markup.add_special(/(((\{.*?\})|\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
end
##
# Converts the CROSSREF +special+ to plain text, removing the suppression
# marker, if any
def handle_special_CROSSREF special
text = special.text
text.sub(/^\\/, '')
end
##
# Converts the TIDYLINK +special+ to just the text part
def handle_special_TIDYLINK special
text = special.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_special_HARD_BREAK ignore
alias start_accepting ignore
end
PK ! i"
include.rbnu [ ##
# 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 ! DKz z document.rbnu [ ##
# 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 ! 1q5 list_item.rbnu [ ##
# 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 ! XT\ rule.rbnu [ ##
# 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 ! \cb attr_changer.rbnu [ 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 ! },$ $ formatter.rbnu [ ##
# 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_special(/
/, :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 special for links of the form rdoc-...:
def add_special_RDOCLINK
@markup.add_special(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
end
##
# Adds a special for links of the form {}[] and []
def add_special_TIDYLINK
@markup.add_special(/(?:
\{.*?\} | # multi-word label
\b[^\s{}]+? # single-word label
)
\[\S+?\] # link target
/x, :TIDYLINK)
end
##
# Add a new set of tags for an attribute. We allow separate start and end
# tags for flexibility
def add_tag(name, start, stop)
attr = @attributes.bitmap_for name
@attr_tags << InlineTag.new(attr, start, stop)
end
##
# Allows +tag+ to be decorated with additional information.
def annotate(tag)
tag
end
##
# Marks up +content+
def convert content
@markup.convert content, self
end
##
# Converts flow items +flow+
def convert_flow(flow)
res = []
flow.each do |item|
case item
when String then
res << convert_string(item)
when RDoc::Markup::AttrChanger then
off_tags res, item
on_tags res, item
when RDoc::Markup::Special then
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res.join
end
##
# Converts added specials. See RDoc::Markup#add_special
def convert_special special
return special.text if in_tt?
handled = false
@attributes.each_name_of special.type do |name|
method_name = "handle_special_#{name}"
if respond_to? method_name then
special.text = send method_name, special
handled = true
end
end
unless handled then
special_name = @attributes.as_string special.type
raise RDoc::Error, "Unhandled special #{special_name}: #{special}"
end
special.text
end
##
# Converts a string to be fancier if desired
def convert_string string
string
end
##
# Use ignore in your subclass to ignore the content of a node.
#
# ##
# # We don't support raw nodes in ToNoRaw
#
# alias accept_raw ignore
def ignore *node
end
##
# Are we currently inside tt tags?
def in_tt?
@in_tt > 0
end
##
# Turns on tags for +item+ on +res+
def on_tags res, item
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0 then
res << annotate(tag.on)
@in_tt += 1 if tt? tag
end
end
end
##
# Turns off tags for +item+ on +res+
def off_tags res, item
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0 then
@in_tt -= 1 if tt? tag
res << annotate(tag.off)
end
end
end
##
# Extracts and a scheme, url and an anchor id from +url+ and returns them.
def parse_url url
case url
when /^rdoc-label:([^:]*)(?::(.*))?/ then
scheme = 'link'
path = "##{$1}"
id = " id=\"#{$2}\"" if $2
when /([A-Za-z]+):(.*)/ then
scheme = $1.downcase
path = $2
when /^#/ then
else
scheme = 'http'
path = url
url = url
end
if scheme == 'link' then
url = if path[0, 1] == '#' then # is this meaningful?
path
else
self.class.gen_relative_url @from_path, path
end
end
[scheme, url, id]
end
##
# Is +tag+ a tt tag?
def tt? tag
tag.bit == @tt_bit
end
end
PK ! z!!y y
to_rdoc.rbnu [ ##
# Outputs RDoc markup as RDoc markup! (mostly)
class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
##
# Current indent amount for output in characters
attr_accessor :indent
##
# Output width in characters
attr_accessor :width
##
# Stack of current list indexes for alphabetic and numeric lists
attr_reader :list_index
##
# Stack of list types
attr_reader :list_type
##
# Stack of list widths for indentation
attr_reader :list_width
##
# Prefix for the next list item. See #use_prefix
attr_reader :prefix
##
# Output accumulator
attr_reader :res
##
# Creates a new formatter that will output (mostly) \RDoc markup
def initialize markup = nil
super nil, markup
@markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
@width = 78
init_tags
@headings = {}
@headings.default = []
@headings[1] = ['= ', '']
@headings[2] = ['== ', '']
@headings[3] = ['=== ', '']
@headings[4] = ['==== ', '']
@headings[5] = ['===== ', '']
@headings[6] = ['====== ', '']
@hard_break = "\n"
end
##
# Maps attributes to HTML sequences
def init_tags
add_tag :BOLD, "", ""
add_tag :TT, "", ""
add_tag :EM, "", ""
end
##
# Adds +blank_line+ to the output
def accept_blank_line blank_line
@res << "\n"
end
##
# Adds +paragraph+ to the output
def accept_block_quote block_quote
@indent += 2
block_quote.parts.each do |part|
@prefix = '> '
part.accept self
end
@indent -= 2
end
##
# Adds +heading+ to the output
def accept_heading heading
use_prefix or @res << ' ' * @indent
@res << @headings[heading.level][0]
@res << attributes(heading.text)
@res << @headings[heading.level][1]
@res << "\n"
end
##
# Finishes consumption of +list+
def accept_list_end list
@list_index.pop
@list_type.pop
@list_width.pop
end
##
# Finishes consumption of +list_item+
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"
2
else
bullet = @list_index.last.to_s
@list_index[-1] = @list_index.last.succ
bullet.length + 2
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" unless bullets.empty?
@prefix = ' ' * @indent
@indent += 2
@prefix << bullets + (' ' * @indent)
else
bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
@prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
width = bullet.length + 1
@indent += width
end
end
##
# Prepares the visitor for consuming +list+
def accept_list_start list
case list.type
when :BULLET then
@list_index << nil
@list_width << 1
when :LABEL, :NOTE then
@list_index << nil
@list_width << 2
when :LALPHA then
@list_index << 'a'
@list_width << list.items.length.to_s.length
when :NUMBER then
@list_index << 1
@list_width << list.items.length.to_s.length
when :UALPHA then
@list_index << 'A'
@list_width << list.items.length.to_s.length
else
raise RDoc::Error, "invalid list type #{list.type}"
end
@list_type << list.type
end
##
# Adds +paragraph+ to the output
def accept_paragraph paragraph
text = paragraph.text @hard_break
wrap attributes text
end
##
# Adds +paragraph+ to the output
def accept_indented_paragraph paragraph
@indent += paragraph.indent
text = paragraph.text @hard_break
wrap attributes text
@indent -= paragraph.indent
end
##
# Adds +raw+ to the output
def accept_raw raw
@res << raw.parts.join("\n")
end
##
# Adds +rule+ to the output
def accept_rule rule
use_prefix or @res << ' ' * @indent
@res << '-' * (@width - @indent)
@res << "\n"
end
##
# Outputs +verbatim+ indented 2 columns
def accept_verbatim verbatim
indent = ' ' * (@indent + 2)
verbatim.parts.each do |part|
@res << indent unless part == "\n"
@res << part
end
@res << "\n" unless @res =~ /\n\z/
end
##
# Applies attribute-specific markup to +text+ using RDoc::AttributeManager
def attributes text
flow = @am.flow text.dup
convert_flow flow
end
##
# Returns the generated output
def end_accepting
@res.join
end
##
# Removes preceding \\ from the suppressed crossref +special+
def handle_special_SUPPRESSED_CROSSREF special
text = special.text
text = text.sub('\\', '') unless in_tt?
text
end
##
# Adds a newline to the output
def handle_special_HARD_BREAK special
"\n"
end
##
# Prepares the visitor for text generation
def start_accepting
@res = [""]
@indent = 0
@prefix = nil
@list_index = []
@list_type = []
@list_width = []
end
##
# Adds the stored #prefix to the output and clears it. Lists generate a
# prefix for later consumption.
def use_prefix
prefix, @prefix = @prefix, nil
@res << prefix if prefix
prefix
end
##
# Wraps +text+ to #width
def wrap text
return unless text && !text.empty?
text_len = @width - @indent
text_len = 20 if text_len < 20
re = /^(.{0,#{text_len}})[ \n]/
next_prefix = ' ' * @indent
prefix = @prefix || next_prefix
@prefix = nil
@res << prefix
while text.length > text_len
if text =~ re then
@res << $1
text.slice!(0, $&.length)
else
@res << text.slice!(0, text_len)
end
@res << "\n" << next_prefix
end
if text.empty? then
@res.pop
@res.pop
else
@res << text
@res << "\n"
end
end
end
PK ! +
to_tt_only.rbnu [ ##
# Extracts sections of text enclosed in plus, tt or code. Used to discover
# undocumented parameters.
class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
##
# Stack of list types
attr_reader :list_type
##
# Output accumulator
attr_reader :res
##
# Creates a new tt-only formatter.
def initialize markup = nil
super nil, markup
add_tag :TT, nil, nil
end
##
# Adds tts from +block_quote+ to the output
def accept_block_quote block_quote
tt_sections block_quote.text
end
##
# Pops the list type for +list+ from #list_type
def accept_list_end list
@list_type.pop
end
##
# Pushes the list type for +list+ onto #list_type
def accept_list_start list
@list_type << list.type
end
##
# Prepares the visitor for consuming +list_item+
def accept_list_item_start list_item
case @list_type.last
when :NOTE, :LABEL then
Array(list_item.label).map do |label|
tt_sections label
end.flatten
end
end
##
# Adds +paragraph+ to the output
def accept_paragraph paragraph
tt_sections(paragraph.text)
end
##
# Does nothing to +markup_item+ because it doesn't have any user-built
# content
def do_nothing markup_item
end
alias accept_blank_line do_nothing # :nodoc:
alias accept_heading do_nothing # :nodoc:
alias accept_list_item_end do_nothing # :nodoc:
alias accept_raw do_nothing # :nodoc:
alias accept_rule do_nothing # :nodoc:
alias accept_verbatim do_nothing # :nodoc:
##
# Extracts tt sections from +text+
def tt_sections text
flow = @am.flow text.dup
flow.each do |item|
case item
when String then
@res << item if in_tt?
when RDoc::Markup::AttrChanger then
off_tags res, item
on_tags res, item
when RDoc::Markup::Special then
@res << convert_special(item) if in_tt? # TODO can this happen?
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# Returns an Array of items that were wrapped in plus, tt or code.
def end_accepting
@res.compact
end
##
# Prepares the visitor for gathering tt sections
def start_accepting
@res = []
@list_type = []
end
end
PK ! 8 raw.rbnu [ ##
# A section of text that is added to the output document as-is
class RDoc::Markup::Raw
##
# The component parts of the list
attr_reader :parts
##
# Creates a new Raw containing +parts+
def initialize *parts
@parts = []
@parts.concat parts
end
##
# Appends +text+
def << text
@parts << text
end
def == other # :nodoc:
self.class == other.class and @parts == other.parts
end
##
# Calls #accept_raw+ on +visitor+
def accept visitor
visitor.accept_raw self
end
##
# Appends +other+'s parts
def merge other
@parts.concat other.parts
end
def pretty_print q # :nodoc:
self.class.name =~ /.*::(\w{1,4})/i
q.group 2, "[#{$1.downcase}: ", ']' do
q.seplist @parts do |part|
q.pp part
end
end
end
##
# Appends +texts+ onto this Paragraph
def push *texts
self.parts.concat texts
end
##
# The raw text
def text
@parts.join ' '
end
end
PK ! G attribute_manager.rbnu [ ##
# Manages changes of attributes in a block of text
class RDoc::Markup::AttributeManager
##
# The NUL character
NULL = "\000".freeze
#--
# We work by substituting non-printing characters in to the text. For now
# I'm assuming that I can substitute a character in the range 0..8 for a 7
# bit character without damaging the encoded string, but this might be
# optimistic
#++
A_PROTECT = 004 # :nodoc:
##
# Special mask character to prevent inline markup handling
PROTECT_ATTR = A_PROTECT.chr # :nodoc:
##
# The attributes enabled for this markup object.
attr_reader :attributes
##
# This maps delimiters that occur around words (such as *bold* or +tt+)
# where the start and end delimiters and the same. This lets us optimize
# the regexp
attr_reader :matching_word_pairs
##
# And this is used when the delimiters aren't the same. In this case the
# hash maps a pattern to the attribute character
attr_reader :word_pair_map
##
# This maps HTML tags to the corresponding attribute char
attr_reader :html_tags
##
# A \ in front of a character that would normally be processed turns off
# processing. We do this by turning \< into <#{PROTECT}
attr_reader :protectable
##
# And this maps _special_ sequences to a name. A special sequence is
# something like a WikiWord
attr_reader :special
##
# Creates a new attribute manager that understands bold, emphasized and
# teletype text.
def initialize
@html_tags = {}
@matching_word_pairs = {}
@protectable = %w[<]
@special = []
@word_pair_map = {}
@attributes = RDoc::Markup::Attributes.new
add_word_pair "*", "*", :BOLD
add_word_pair "_", "_", :EM
add_word_pair "+", "+", :TT
add_html "em", :EM
add_html "i", :EM
add_html "b", :BOLD
add_html "tt", :TT
add_html "code", :TT
end
##
# Return an attribute object with the given turn_on and turn_off bits set
def attribute(turn_on, turn_off)
RDoc::Markup::AttrChanger.new turn_on, turn_off
end
##
# Changes the current attribute from +current+ to +new+
def change_attribute current, new
diff = current ^ new
attribute(new & diff, current & diff)
end
##
# Used by the tests to change attributes by name from +current_set+ to
# +new_set+
def changed_attribute_by_name current_set, new_set
current = new = 0
current_set.each do |name|
current |= @attributes.bitmap_for(name)
end
new_set.each do |name|
new |= @attributes.bitmap_for(name)
end
change_attribute(current, new)
end
##
# Copies +start_pos+ to +end_pos+ from the current string
def copy_string(start_pos, end_pos)
res = @str[start_pos...end_pos]
res.gsub!(/\000/, '')
res
end
##
# Map attributes like textto the sequence
# \001\002\001\003, where is a per-attribute specific
# character
def convert_attrs(str, attrs)
# first do matching ones
tags = @matching_word_pairs.keys.join("")
re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
1 while str.gsub!(re) do
attr = @matching_word_pairs[$2]
attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
$1 + NULL * $2.length + $3 + NULL * $2.length + $4
end
# then non-matching
unless @word_pair_map.empty? then
@word_pair_map.each do |regexp, attr|
str.gsub!(regexp) {
attrs.set_attrs($`.length + $1.length, $2.length, attr)
NULL * $1.length + $2 + NULL * $3.length
}
end
end
end
##
# Converts HTML tags to RDoc attributes
def convert_html(str, attrs)
tags = @html_tags.keys.join '|'
1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
attr = @html_tags[$1.downcase]
html_length = $1.length + 2
seq = NULL * html_length
attrs.set_attrs($`.length + html_length, $2.length, attr)
seq + $2 + seq + NULL
}
end
##
# Converts special sequences to RDoc attributes
def convert_specials str, attrs
@special.each do |regexp, attribute|
str.scan(regexp) do
capture = $~.size == 1 ? 0 : 1
s, e = $~.offset capture
attrs.set_attrs s, e - s, attribute | @attributes.special
end
end
end
##
# Escapes special sequences of text to prevent conversion to RDoc
def mask_protected_sequences
# protect __send__, __FILE__, etc.
@str.gsub!(/__([a-z]+)__/i,
"_#{PROTECT_ATTR}_#{PROTECT_ATTR}\\1_#{PROTECT_ATTR}_#{PROTECT_ATTR}")
@str.gsub!(/(\A|[^\\])\\([#{Regexp.escape @protectable.join}])/m,
"\\1\\2#{PROTECT_ATTR}")
@str.gsub!(/\\(\\[#{Regexp.escape @protectable.join}])/m, "\\1")
end
##
# Unescapes special sequences of text
def unmask_protected_sequences
@str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
end
##
# Adds a markup class with +name+ for words wrapped in the +start+ and
# +stop+ character. To make words wrapped with "*" bold:
#
# am.add_word_pair '*', '*', :BOLD
def add_word_pair(start, stop, name)
raise ArgumentError, "Word flags may not start with '<'" if
start[0,1] == '<'
bitmap = @attributes.bitmap_for name
if start == stop then
@matching_word_pairs[start] = bitmap
else
pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/
@word_pair_map[pattern] = bitmap
end
@protectable << start[0,1]
@protectable.uniq!
end
##
# Adds a markup class with +name+ for words surrounded by HTML tag +tag+.
# To process emphasis tags:
#
# am.add_html 'em', :EM
def add_html(tag, name)
@html_tags[tag.downcase] = @attributes.bitmap_for name
end
##
# Adds a special handler for +pattern+ with +name+. A simple URL handler
# would be:
#
# @am.add_special(/((https?:)\S+\w)/, :HYPERLINK)
def add_special pattern, name
@special << [pattern, @attributes.bitmap_for(name)]
end
##
# Processes +str+ converting attributes, HTML and specials
def flow str
@str = str
mask_protected_sequences
@attrs = RDoc::Markup::AttrSpan.new @str.length
convert_attrs @str, @attrs
convert_html @str, @attrs
convert_specials @str, @attrs
unmask_protected_sequences
split_into_flow
end
##
# Debug method that prints a string along with its attributes
def display_attributes
puts
puts @str.tr(NULL, "!")
bit = 1
16.times do |bno|
line = ""
@str.length.times do |i|
if (@attrs[i] & bit) == 0
line << " "
else
if bno.zero?
line << "S"
else
line << ("%d" % (bno+1))
end
end
end
puts(line) unless line =~ /^ *$/
bit <<= 1
end
end
##
# Splits the string into chunks by attribute change
def split_into_flow
res = []
current_attr = 0
str_len = @str.length
# skip leading invisible text
i = 0
i += 1 while i < str_len and @str[i].chr == "\0"
start_pos = i
# then scan the string, chunking it on attribute changes
while i < str_len
new_attr = @attrs[i]
if new_attr != current_attr
if i > start_pos
res << copy_string(start_pos, i)
start_pos = i
end
res << change_attribute(current_attr, new_attr)
current_attr = new_attr
if (current_attr & @attributes.special) != 0 then
i += 1 while
i < str_len and (@attrs[i] & @attributes.special) != 0
res << RDoc::Markup::Special.new(current_attr,
copy_string(start_pos, i))
start_pos = i
next
end
end
# move on, skipping any invisible characters
begin
i += 1
end while i < str_len and @str[i].chr == "\0"
end
# tidy up trailing text
if start_pos < str_len
res << copy_string(start_pos, str_len)
end
# and reset to all attributes off
res << change_attribute(current_attr, 0) if current_attr != 0
res
end
end
PK ! ծA
hard_break.rbnu [ ##
# A hard-break in the middle of a paragraph.
class RDoc::Markup::HardBreak
@instance = new
##
# RDoc::Markup::HardBreak is a singleton
def self.new
@instance
end
##
# Calls #accept_hard_break on +visitor+
def accept visitor
visitor.accept_hard_break self
end
def == other # :nodoc:
self.class === other
end
def pretty_print q # :nodoc:
q.text "[break]"
end
end
PK ! vai i
blank_line.rbnu [ ##
# An empty line. This class is a singleton.
class RDoc::Markup::BlankLine
@instance = new
##
# RDoc::Markup::BlankLine is a singleton
def self.new
@instance
end
##
# Calls #accept_blank_line on +visitor+
def accept visitor
visitor.accept_blank_line self
end
def pretty_print q # :nodoc:
q.text 'blankline'
end
end
PK ! block_quote.rbnu [ ##
# A quoted section which contains markup items.
class RDoc::Markup::BlockQuote < RDoc::Markup::Raw
##
# Calls #accept_block_quote on +visitor+
def accept visitor
visitor.accept_block_quote self
end
end
PK ! >.> to_table_of_contents.rbnu [ ##
# Extracts just the RDoc::Markup::Heading elements from a
# RDoc::Markup::Document to help build a table of contents
class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter
@to_toc = nil
##
# Singleton for table-of-contents generation
def self.to_toc
@to_toc ||= new
end
##
# Output accumulator
attr_reader :res
##
# Omits headings with a level less than the given level.
attr_accessor :omit_headings_below
def initialize # :nodoc:
super nil
@omit_headings_below = nil
end
##
# Adds +document+ to the output, using its heading cutoff if present
def accept_document document
@omit_headings_below = document.omit_headings_below
super
end
##
# Adds +heading+ to the table of contents
def accept_heading heading
@res << heading unless suppressed? heading
end
##
# Returns the table of contents
def end_accepting
@res
end
##
# Prepares the visitor for text generation
def start_accepting
@omit_headings_below = nil
@res = []
end
##
# Returns true if +heading+ is below the display threshold
def suppressed? heading
return false unless @omit_headings_below
heading.level > @omit_headings_below
end
# :stopdoc:
alias accept_block_quote ignore
alias accept_raw ignore
alias accept_rule ignore
alias accept_blank_line ignore
alias accept_paragraph ignore
alias accept_verbatim ignore
alias accept_list_end ignore
alias accept_list_item_start ignore
alias accept_list_item_end ignore
alias accept_list_end_bullet ignore
alias accept_list_start ignore
# :startdoc:
end
PK ! RR*/ / to_html_crossref.rbnu [ ##
# Subclass of the RDoc::Markup::ToHtml class that supports looking up method
# names, classes, etc to create links. RDoc::CrossReference is used to
# generate those links based on the current context.
class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# :stopdoc:
ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP
CLASS_REGEXP_STR = RDoc::CrossReference::CLASS_REGEXP_STR
CROSSREF_REGEXP = RDoc::CrossReference::CROSSREF_REGEXP
METHOD_REGEXP_STR = RDoc::CrossReference::METHOD_REGEXP_STR
# :startdoc:
##
# RDoc::CodeObject for generating references
attr_accessor :context
##
# Should we show '#' characters on method references?
attr_accessor :show_hash
##
# Creates a new crossref resolver that generates links relative to +context+
# which lives at +from_path+ in the generated files. '#' characters on
# references are removed unless +show_hash+ is true. Only method names
# preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
def initialize(options, from_path, context, markup = nil)
raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super options, markup
@context = context
@from_path = from_path
@hyperlink_all = @options.hyperlink_all
@show_hash = @options.show_hash
crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
@markup.add_special crossref_re, :CROSSREF
@cross_reference = RDoc::CrossReference.new @context
end
##
# Creates a link to the reference +name+ if the name exists. If +text+ is
# given it is used as the link text, otherwise +name+ is used.
def cross_reference name, text = nil
lookup = name
name = name[1..-1] unless @show_hash if name[0, 1] == '#'
name = "#{CGI.unescape $'} at #{$1}" if name =~ /(.*[^#:])@/
text = name unless text
link lookup, text
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_special_CROSSREF(special)
name = special.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_special_HYPERLINK special
return cross_reference $' if special.text =~ /\Ardoc-ref:/
super
end
##
# +special+ 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_special_RDOCLINK]
def handle_special_RDOCLINK special
url = special.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:/
cross_reference $', text
end
##
# Creates an HTML link to +name+ with the given +text+.
def link name, text
original_name = name
if name =~ /(.*[^#:])@/ then
name = $1
label = $'
end
ref = @cross_reference.resolve name, text
text = ref.output_name @context if
RDoc::MethodAttr === ref and text == original_name
case ref
when String then
ref
else
path = ref.as_href @from_path
if path =~ /#/ then
path << "-label-#{label}"
elsif ref.sections and
ref.sections.any? { |section| label == section.title } then
path << "##{label}"
else
path << "#label-#{label}"
end if label
"#{text}"
end
end
end
PK ! u verbatim.rbnu [ ##
# 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 ! [Ba 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:
"#" % [
object_id, @type, text.dump]
end
def to_s # :nodoc:
"RegexpHandling: type=#{type} text=#{text.dump}"
end
end
PK ! S: paragraph.rbnu [ ##
# A Paragraph of text
class RDoc::Markup::Paragraph < RDoc::Markup::Raw
##
# Calls #accept_paragraph on +visitor+
def accept visitor
visitor.accept_paragraph self
end
##
# Joins the raw paragraph text and converts inline HardBreaks to the
# +hard_break+ text.
def text hard_break = ''
@parts.map do |part|
if RDoc::Markup::HardBreak === part then
hard_break
else
part
end
end.join
end
end
PK ! aD pre_process.rbnu [ ##
# Handle common directives that can occur in a block of text:
#
# \:include: filename
#
# Directives can be escaped by preceding them with a backslash.
#
# RDoc plugin authors can register additional directives to be handled by
# using RDoc::Markup::PreProcess::register.
#
# Any directive that is not built-in to RDoc (including those registered via
# plugins) will be stored in the metadata hash on the CodeObject the comment
# is attached to. See RDoc::Markup@Directives for the list of built-in
# directives.
class RDoc::Markup::PreProcess
##
# An RDoc::Options instance that will be filled in with overrides from
# directives
attr_accessor :options
##
# Adds a post-process handler for directives. The handler will be called
# with the result RDoc::Comment (or text String) and the code object for the
# comment (if any).
def self.post_process &block
@post_processors << block
end
##
# Registered post-processors
def self.post_processors
@post_processors
end
##
# Registers +directive+ as one handled by RDoc. If a block is given the
# directive will be replaced by the result of the block, otherwise the
# directive will be removed from the processed text.
#
# The block will be called with the directive name and the directive
# parameter:
#
# RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|
# # replace text, etc.
# end
def self.register directive, &block
@registered[directive] = block
end
##
# Registered directives
def self.registered
@registered
end
##
# Clears all registered directives and post-processors
def self.reset
@post_processors = []
@registered = {}
end
reset
##
# Creates a new pre-processor for +input_file_name+ that will look for
# included files in +include_path+
def initialize(input_file_name, include_path)
@input_file_name = input_file_name
@include_path = include_path
@options = nil
end
##
# Look for directives in the given +text+.
#
# Options that we don't handle are yielded. If the block returns false the
# directive is restored to the text. If the block returns nil or no block
# was given the directive is handled according to the registered directives.
# If a String was returned the directive is replaced with the string.
#
# If no matching directive was registered the directive is restored to the
# text.
#
# If +code_object+ is given and the directive is unknown then the
# directive's parameter is set as metadata on the +code_object+. See
# RDoc::CodeObject#metadata for details.
def handle text, code_object = nil, &block
if RDoc::Comment === text then
comment = text
text = text.text
end
encoding = text.encoding if defined?(Encoding)
# regexp helper (square brackets for optional)
# $1 $2 $3 $4 $5
# [prefix][\]:directive:[spaces][param]newline
text.gsub!(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?(\r?\n|$)/) do
# skip something like ':toto::'
next $& if $4.empty? and $5 and $5[0, 1] == ':'
# skip if escaped
next "#$1:#$3:#$4#$5\n" unless $2.empty?
# This is not in handle_directive because I didn't want to pass another
# argument into it
if comment and $3 == 'markup' then
next "#{$1.strip}\n" unless $5
comment.format = $5.downcase
next "#{$1.strip}\n"
end
handle_directive $1, $3, $5, code_object, encoding, &block
end
comment = text unless comment
self.class.post_processors.each do |handler|
handler.call comment, code_object
end
text
end
##
# Performs the actions described by +directive+ and its parameter +param+.
#
# +code_object+ is used for directives that operate on a class or module.
# +prefix+ is used to ensure the replacement for handled directives is
# correct. +encoding+ is used for the include directive.
#
# For a list of directives in RDoc see RDoc::Markup.
#--
# When 1.8.7 support is ditched prefix can be defaulted to ''
def handle_directive prefix, directive, param, code_object = nil,
encoding = nil
blankline = "#{prefix.strip}\n"
directive = directive.downcase
case directive
when 'arg', 'args' then
return "#{prefix}:#{directive}: #{param}\n" unless code_object
code_object.params = param
blankline
when 'category' then
if RDoc::Context === code_object then
section = code_object.add_section param
code_object.temporary_section = section
end
blankline # ignore category if we're not on an RDoc::Context
when 'doc' then
return blankline unless code_object
code_object.document_self = true
code_object.force_documentation = true
blankline
when 'enddoc' then
return blankline unless code_object
code_object.done_documenting = true
blankline
when 'include' then
filename = param.split.first
include_file filename, prefix, encoding
when 'main' then
@options.main_page = param if @options.respond_to? :main_page
blankline
when 'nodoc' then
return blankline unless code_object
code_object.document_self = nil # notify nodoc
code_object.document_children = param !~ /all/i
blankline
when 'notnew', 'not_new', 'not-new' then
return blankline unless RDoc::AnyMethod === code_object
code_object.dont_rename_initialize = true
blankline
when 'startdoc' then
return blankline unless code_object
code_object.start_doc
code_object.force_documentation = true
blankline
when 'stopdoc' then
return blankline unless code_object
code_object.stop_doc
blankline
when 'title' then
@options.default_title = param if @options.respond_to? :default_title=
blankline
when 'yield', 'yields' then
return blankline unless code_object
# remove parameter &block
code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params
code_object.block_params = param
blankline
else
result = yield directive, param if block_given?
case result
when nil then
code_object.metadata[directive] = param if code_object
if RDoc::Markup::PreProcess.registered.include? directive then
handler = RDoc::Markup::PreProcess.registered[directive]
result = handler.call directive, param if handler
else
result = "#{prefix}:#{directive}: #{param}\n"
end
when false then
result = "#{prefix}:#{directive}: #{param}\n"
end
result
end
end
##
# Handles the :include: _filename_ directive.
#
# If the first line of the included file starts with '#', and contains
# an encoding information in the form 'coding:' or 'coding=', it is
# removed.
#
# If all lines in the included file start with a '#', this leading '#'
# is removed before inclusion. The included content is indented like
# the :include: directive.
#--
# so all content will be verbatim because of the likely space after '#'?
# TODO shift left the whole file content in that case
# TODO comment stop/start #-- and #++ in included file must be processed here
def include_file name, indent, encoding
full_name = find_include_file name
unless full_name then
warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
return ''
end
content = RDoc::Encoding.read_file full_name, encoding, true
# strip magic comment
content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
# strip leading '#'s, but only if all lines start with them
if content =~ /^[^#]/ then
content.gsub(/^/, indent)
else
content.gsub(/^#?/, indent)
end
end
##
# Look for the given file in the directory containing the current file,
# and then in each of the directories specified in the RDOC_INCLUDE path
def find_include_file(name)
to_search = [File.dirname(@input_file_name)].concat @include_path
to_search.each do |dir|
full_name = File.join(dir, name)
stat = File.stat(full_name) rescue next
return full_name if stat.readable?
end
nil
end
end
PK ! !] attr_span.rbnu [ ##
# An array of attributes which parallels the characters in a string.
class RDoc::Markup::AttrSpan
##
# Creates a new AttrSpan for +length+ characters
def initialize(length)
@attrs = Array.new(length, 0)
end
##
# Toggles +bits+ from +start+ to +length+
def set_attrs(start, length, bits)
for i in start ... (start+length)
@attrs[i] |= bits
end
end
##
# Accesses flags for character +n+
def [](n)
@attrs[n]
end
end
PK ! 8 8 parser.rbnu [ require 'strscan'
##
# A recursive-descent parser for RDoc markup.
#
# The parser tokenizes an input string then parses the tokens into a Document.
# Documents can be converted into output formats by writing a visitor like
# RDoc::Markup::ToHTML.
#
# The parser only handles the block-level constructs Paragraph, List,
# ListItem, Heading, Verbatim, BlankLine and Rule. Inline markup such as
# \+blah\+ is handled separately by RDoc::Markup::AttributeManager.
#
# To see what markup the Parser implements read RDoc. To see how to use
# RDoc markup to format text in your program read RDoc::Markup.
class RDoc::Markup::Parser
include RDoc::Text
##
# List token types
LIST_TOKENS = [
:BULLET,
:LABEL,
:LALPHA,
:NOTE,
:NUMBER,
:UALPHA,
]
##
# Parser error subclass
class Error < RuntimeError; end
##
# Raised when the parser is unable to handle the given markup
class ParseError < Error; end
##
# Enables display of debugging information
attr_accessor :debug
##
# Token accessor
attr_reader :tokens
##
# Parses +str+ into a Document.
#
# Use RDoc::Markup#parse instead of this method.
def self.parse str
parser = new
parser.tokenize str
doc = RDoc::Markup::Document.new
parser.parse doc
end
##
# Returns a token stream for +str+, for testing
def self.tokenize str
parser = new
parser.tokenize str
parser.tokens
end
##
# Creates a new Parser. See also ::parse
def initialize
@binary_input = nil
@current_token = nil
@debug = false
@have_encoding = Object.const_defined? :Encoding
@have_byteslice = ''.respond_to? :byteslice
@input = nil
@input_encoding = nil
@line = 0
@line_pos = 0
@s = nil
@tokens = []
end
##
# Builds a Heading of +level+
def build_heading level
type, text, = get
text = case type
when :TEXT then
skip :NEWLINE
text
else
unget
''
end
RDoc::Markup::Heading.new level, text
end
##
# Builds a List flush to +margin+
def build_list margin
p :list_start => margin if @debug
list = RDoc::Markup::List.new
label = nil
until @tokens.empty? do
type, data, column, = get
case type
when *LIST_TOKENS then
if column < margin || (list.type && list.type != type) then
unget
break
end
list.type = type
peek_type, _, column, = peek_token
case type
when :NOTE, :LABEL then
label = [] unless label
if peek_type == :NEWLINE then
# description not on the same line as LABEL/NOTE
# skip the trailing newline & any blank lines below
while peek_type == :NEWLINE
get
peek_type, _, column, = peek_token
end
# we may be:
# - at end of stream
# - at a column < margin:
# [text]
# blah blah blah
# - at the same column, but with a different type of list item
# [text]
# * blah blah
# - at the same column, with the same type of list item
# [one]
# [two]
# In all cases, we have an empty description.
# In the last case only, we continue.
if peek_type.nil? || column < margin then
empty = true
elsif column == margin then
case peek_type
when type
empty = :continue
when *LIST_TOKENS
empty = true
else
empty = false
end
else
empty = false
end
if empty then
label << data
next if empty == :continue
break
end
end
else
data = nil
end
if label then
data = label << data
label = nil
end
list_item = RDoc::Markup::ListItem.new data
parse list_item, column
list << list_item
else
unget
break
end
end
p :list_end => margin if @debug
if list.empty? then
return nil unless label
return nil unless [:LABEL, :NOTE].include? list.type
list_item = RDoc::Markup::ListItem.new label, RDoc::Markup::BlankLine.new
list << list_item
end
list
end
##
# Builds a Paragraph that is flush to +margin+
def build_paragraph margin
p :paragraph_start => margin if @debug
paragraph = RDoc::Markup::Paragraph.new
until @tokens.empty? do
type, data, column, = get
if type == :TEXT and column == margin then
paragraph << data
break if peek_token.first == :BREAK
data << ' ' if skip :NEWLINE
else
unget
break
end
end
paragraph.parts.last.sub!(/ \z/, '') # cleanup
p :paragraph_end => margin if @debug
paragraph
end
##
# Builds a Verbatim that is indented from +margin+.
#
# The verbatim block is shifted left (the least indented lines start in
# column 0). Each part of the verbatim is one line of text, always
# terminated by a newline. Blank lines always consist of a single newline
# character, and there is never a single newline at the end of the verbatim.
def build_verbatim margin
p :verbatim_begin => margin if @debug
verbatim = RDoc::Markup::Verbatim.new
min_indent = nil
generate_leading_spaces = true
line = ''
until @tokens.empty? do
type, data, column, = get
if type == :NEWLINE then
line << data
verbatim << line
line = ''
generate_leading_spaces = true
next
end
if column <= margin
unget
break
end
if generate_leading_spaces then
indent = column - margin
line << ' ' * indent
min_indent = indent if min_indent.nil? || indent < min_indent
generate_leading_spaces = false
end
case type
when :HEADER then
line << '=' * data
_, _, peek_column, = peek_token
peek_column ||= column + data
indent = peek_column - column - data
line << ' ' * indent
when :RULE then
width = 2 + data
line << '-' * width
_, _, peek_column, = peek_token
peek_column ||= column + width
indent = peek_column - column - width
line << ' ' * indent
when :BREAK, :TEXT then
line << data
else # *LIST_TOKENS
list_marker = case type
when :BULLET then data
when :LABEL then "[#{data}]"
when :NOTE then "#{data}::"
else # :LALPHA, :NUMBER, :UALPHA
"#{data}."
end
line << list_marker
peek_type, _, peek_column = peek_token
unless peek_type == :NEWLINE then
peek_column ||= column + list_marker.length
indent = peek_column - column - list_marker.length
line << ' ' * indent
end
end
end
verbatim << line << "\n" unless line.empty?
verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0
verbatim.normalize
p :verbatim_end => margin if @debug
verbatim
end
##
# The character offset for the input string at the given +byte_offset+
def char_pos byte_offset
if @have_byteslice then
@input.byteslice(0, byte_offset).length
elsif @have_encoding then
matched = @binary_input[0, byte_offset]
matched.force_encoding @input_encoding
matched.length
else
byte_offset
end
end
##
# Pulls the next token from the stream.
def get
@current_token = @tokens.shift
p :get => @current_token if @debug
@current_token
end
##
# Parses the tokens into an array of RDoc::Markup::XXX objects,
# and appends them to the passed +parent+ RDoc::Markup::YYY object.
#
# Exits at the end of the token stream, or when it encounters a token
# in a column less than +indent+ (unless it is a NEWLINE).
#
# Returns +parent+.
def parse parent, indent = 0
p :parse_start => indent if @debug
until @tokens.empty? do
type, data, column, = get
case type
when :BREAK then
parent << RDoc::Markup::BlankLine.new
skip :NEWLINE, false
next
when :NEWLINE then
# trailing newlines are skipped below, so this is a blank line
parent << RDoc::Markup::BlankLine.new
skip :NEWLINE, false
next
end
# indentation change: break or verbatim
if column < indent then
unget
break
elsif column > indent then
unget
parent << build_verbatim(indent)
next
end
# indentation is the same
case type
when :HEADER then
parent << build_heading(data)
when :RULE then
parent << RDoc::Markup::Rule.new(data)
skip :NEWLINE
when :TEXT then
unget
parse_text parent, indent
when *LIST_TOKENS then
unget
parent << build_list(indent)
else
type, data, column, line = @current_token
raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
end
end
p :parse_end => indent if @debug
parent
end
##
# Small hook that is overridden by RDoc::TomDoc
def parse_text parent, indent # :nodoc:
parent << build_paragraph(indent)
end
##
# Returns the next token on the stream without modifying the stream
def peek_token
token = @tokens.first || []
p :peek => token if @debug
token
end
##
# Creates the StringScanner
def setup_scanner input
@line = 0
@line_pos = 0
@input = input.dup
if @have_encoding and not @have_byteslice then
@input_encoding = @input.encoding
@binary_input = @input.force_encoding Encoding::BINARY
end
@s = StringScanner.new input
end
##
# Skips the next token if its type is +token_type+.
#
# Optionally raises an error if the next token is not of the expected type.
def skip token_type, error = true
type, = get
return unless type # end of stream
return @current_token if token_type == type
unget
raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
end
##
# Turns text +input+ into a stream of tokens
def tokenize input
setup_scanner input
until @s.eos? do
pos = @s.pos
# leading spaces will be reflected by the column of the next token
# the only thing we loose are trailing spaces at the end of the file
next if @s.scan(/ +/)
# note: after BULLET, LABEL, etc.,
# indent will be the column of the next non-newline token
@tokens << case
# [CR]LF => :NEWLINE
when @s.scan(/\r?\n/) then
token = [:NEWLINE, @s.matched, *token_pos(pos)]
@line_pos = char_pos @s.pos
@line += 1
token
# === text => :HEADER then :TEXT
when @s.scan(/(=+)(\s*)/) then
level = @s[1].length
header = [:HEADER, level, *token_pos(pos)]
if @s[2] =~ /^\r?\n/ then
@s.pos -= @s[2].length
header
else
pos = @s.pos
@s.scan(/.*/)
@tokens << header
[:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)]
end
# --- (at least 3) and nothing else on the line => :RULE
when @s.scan(/(-{3,}) *\r?$/) then
[:RULE, @s[1].length - 2, *token_pos(pos)]
# * or - followed by white space and text => :BULLET
when @s.scan(/([*-]) +(\S)/) then
@s.pos -= @s[2].bytesize # unget \S
[:BULLET, @s[1], *token_pos(pos)]
# A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER
when @s.scan(/([a-z]|\d+)\. +(\S)/i) then
# FIXME if tab(s), the column will be wrong
# either support tabs everywhere by first expanding them to
# spaces, or assume that they will have been replaced
# before (and provide a check for that at least in debug
# mode)
list_label = @s[1]
@s.pos -= @s[2].bytesize # unget \S
list_type =
case list_label
when /[a-z]/ then :LALPHA
when /[A-Z]/ then :UALPHA
when /\d/ then :NUMBER
else
raise ParseError, "BUG token #{list_label}"
end
[list_type, list_label, *token_pos(pos)]
# [text] followed by spaces or end of line => :LABEL
when @s.scan(/\[(.*?)\]( +|\r?$)/) then
[:LABEL, @s[1], *token_pos(pos)]
# text:: followed by spaces or end of line => :NOTE
when @s.scan(/(.*?)::( +|\r?$)/) then
[:NOTE, @s[1], *token_pos(pos)]
# anything else: :TEXT
else @s.scan(/(.*?)( )?\r?$/)
token = [:TEXT, @s[1], *token_pos(pos)]
if @s[2] then
@tokens << token
[:BREAK, @s[2], *token_pos(pos + @s[1].length)]
else
token
end
end
end
self
end
##
# Calculates the column (by character) and line of the current token based
# on +byte_offset+.
def token_pos byte_offset
offset = char_pos byte_offset
[offset - @line_pos, @line]
end
##
# Returns the current token to the token stream
def unget
token = @current_token
p :unget => token if @debug
raise Error, 'too many #ungets' if token == @tokens.first
@tokens.unshift token if token
end
end
PK ! AWDaC aC formatter_test_case.rbnu [ require 'minitest/unit'
##
# Test case for creating new RDoc::Markup formatters. See
# test/test_rdoc_markup_to_*.rb for examples.
#
# This test case adds a variety of tests to your subclass when
# #add_visitor_tests is called. Most tests set up a scenario then call a
# method you will provide to perform the assertion on the output.
#
# Your subclass must instantiate a visitor and assign it to @to.
#
# For example, test_accept_blank_line sets up a RDoc::Markup::BlockLine then
# calls accept_blank_line on your visitor. You are responsible for asserting
# that the output is correct.
#
# Example:
#
# class TestRDocMarkupToNewFormat < RDoc::Markup::FormatterTestCase
#
# add_visitor_tests
#
# def setup
# super
#
# @to = RDoc::Markup::ToNewFormat.new
# end
#
# def accept_blank_line
# assert_equal :junk, @to.res.join
# end
#
# # ...
#
# end
class RDoc::Markup::FormatterTestCase < RDoc::TestCase
##
# Call #setup when inheriting from this test case.
#
# Provides the following instance variables:
#
# +@m+:: RDoc::Markup.new
# +@RM+:: RDoc::Markup # to reduce typing
# +@bullet_list+:: @RM::List.new :BULLET, # ...
# +@label_list+:: @RM::List.new :LABEL, # ...
# +@lalpha_list+:: @RM::List.new :LALPHA, # ...
# +@note_list+:: @RM::List.new :NOTE, # ...
# +@number_list+:: @RM::List.new :NUMBER, # ...
# +@ualpha_list+:: @RM::List.new :UALPHA, # ...
def setup
super
@options = RDoc::Options.new
@m = @RM.new
@bullet_list = @RM::List.new(:BULLET,
@RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
@label_list = @RM::List.new(:LABEL,
@RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')),
@RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too')))
@lalpha_list = @RM::List.new(:LALPHA,
@RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
@note_list = @RM::List.new(:NOTE,
@RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')),
@RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too')))
@number_list = @RM::List.new(:NUMBER,
@RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
@ualpha_list = @RM::List.new(:UALPHA,
@RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
end
##
# Call to add the visitor tests to your test case
def self.add_visitor_tests
class_eval do
##
# Calls start_accepting which needs to verify startup state
def test_start_accepting
@to.start_accepting
start_accepting
end
##
# Calls end_accepting on your test case which needs to call
# @to.end_accepting and verify document generation
def test_end_accepting
@to.start_accepting
@to.res << 'hi'
end_accepting
end
##
# Calls accept_blank_line
def test_accept_blank_line
@to.start_accepting
@to.accept_blank_line @RM::BlankLine.new
accept_blank_line
end
##
# Calls accept_block_quote
def test_accept_block_quote
@to.start_accepting
@to.accept_block_quote block para 'quote'
accept_block_quote
end
##
# Test case that calls @to.accept_document
def test_accept_document
@to.start_accepting
@to.accept_document @RM::Document.new @RM::Paragraph.new 'hello'
accept_document
end
##
# Calls accept_heading with a level 5 RDoc::Markup::Heading
def test_accept_heading
@to.start_accepting
@to.accept_heading @RM::Heading.new(5, 'Hello')
accept_heading
end
##
# Calls accept_heading_1 with a level 1 RDoc::Markup::Heading
def test_accept_heading_1
@to.start_accepting
@to.accept_heading @RM::Heading.new(1, 'Hello')
accept_heading_1
end
##
# Calls accept_heading_2 with a level 2 RDoc::Markup::Heading
def test_accept_heading_2
@to.start_accepting
@to.accept_heading @RM::Heading.new(2, 'Hello')
accept_heading_2
end
##
# Calls accept_heading_3 with a level 3 RDoc::Markup::Heading
def test_accept_heading_3
# HACK this doesn't belong here
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
@to.start_accepting
@to.accept_heading @RM::Heading.new(3, 'Hello')
accept_heading_3
end
##
# Calls accept_heading_4 with a level 4 RDoc::Markup::Heading
def test_accept_heading_4
@to.start_accepting
@to.accept_heading @RM::Heading.new(4, 'Hello')
accept_heading_4
end
##
# Calls accept_heading_b with a bold level 1 RDoc::Markup::Heading
def test_accept_heading_b
@to.start_accepting
@to.accept_heading @RM::Heading.new(1, '*Hello*')
accept_heading_b
end
##
# Calls accept_heading_suppressed_crossref with a level 1
# RDoc::Markup::Heading containing a suppressed crossref
def test_accept_heading_suppressed_crossref # HACK to_html_crossref test
@to.start_accepting
@to.accept_heading @RM::Heading.new(1, '\\Hello')
accept_heading_suppressed_crossref
end
##
# Calls accept_paragraph
def test_accept_paragraph
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('hi')
accept_paragraph
end
##
# Calls accept_paragraph_b with a RDoc::Markup::Paragraph containing
# bold words
def test_accept_paragraph_b
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('reg bold words reg')
accept_paragraph_b
end
##
# Calls accept_paragraph_br with a RDoc::Markup::Paragraph containing
# a \
def test_accept_paragraph_br
@to.start_accepting
@to.accept_paragraph para 'one
two'
accept_paragraph_br
end
##
# Calls accept_paragraph with a Paragraph containing a hard break
def test_accept_paragraph_break
@to.start_accepting
@to.accept_paragraph para('hello', hard_break, 'world')
accept_paragraph_break
end
##
# Calls accept_paragraph_i with a RDoc::Markup::Paragraph containing
# emphasized words
def test_accept_paragraph_i
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('reg italic words reg')
accept_paragraph_i
end
##
# Calls accept_paragraph_plus with a RDoc::Markup::Paragraph containing
# teletype words
def test_accept_paragraph_plus
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('reg +teletype+ reg')
accept_paragraph_plus
end
##
# Calls accept_paragraph_star with a RDoc::Markup::Paragraph containing
# bold words
def test_accept_paragraph_star
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('reg *bold* reg')
accept_paragraph_star
end
##
# Calls accept_paragraph_underscore with a RDoc::Markup::Paragraph
# containing emphasized words
def test_accept_paragraph_underscore
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('reg _italic_ reg')
accept_paragraph_underscore
end
##
# Calls accept_verbatim with a RDoc::Markup::Verbatim
def test_accept_verbatim
@to.start_accepting
@to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
accept_verbatim
end
##
# Calls accept_raw with a RDoc::Markup::Raw
def test_accept_raw
@to.start_accepting
@to.accept_raw @RM::Raw.new("",
"| Name | Count",
" |
|---|
| a | 1",
" |
| b | 2",
" |
")
accept_raw
end
##
# Calls accept_rule with a RDoc::Markup::Rule
def test_accept_rule
@to.start_accepting
@to.accept_rule @RM::Rule.new(4)
accept_rule
end
##
# Calls accept_list_item_start_bullet
def test_accept_list_item_start_bullet
@to.start_accepting
@to.accept_list_start @bullet_list
@to.accept_list_item_start @bullet_list.items.first
accept_list_item_start_bullet
end
##
# Calls accept_list_item_start_label
def test_accept_list_item_start_label
@to.start_accepting
@to.accept_list_start @label_list
@to.accept_list_item_start @label_list.items.first
accept_list_item_start_label
end
##
# Calls accept_list_item_start_lalpha
def test_accept_list_item_start_lalpha
@to.start_accepting
@to.accept_list_start @lalpha_list
@to.accept_list_item_start @lalpha_list.items.first
accept_list_item_start_lalpha
end
##
# Calls accept_list_item_start_note
def test_accept_list_item_start_note
@to.start_accepting
@to.accept_list_start @note_list
@to.accept_list_item_start @note_list.items.first
accept_list_item_start_note
end
##
# Calls accept_list_item_start_note_2
def test_accept_list_item_start_note_2
list = list(:NOTE,
item('teletype',
para('teletype description')))
@to.start_accepting
list.accept @to
@to.end_accepting
accept_list_item_start_note_2
end
##
# Calls accept_list_item_start_note_multi_description
def test_accept_list_item_start_note_multi_description
list = list(:NOTE,
item(%w[label],
para('description one')),
item(nil, para('description two')))
@to.start_accepting
list.accept @to
@to.end_accepting
accept_list_item_start_note_multi_description
end
##
# Calls accept_list_item_start_note_multi_label
def test_accept_list_item_start_note_multi_label
list = list(:NOTE,
item(%w[one two],
para('two headers')))
@to.start_accepting
list.accept @to
@to.end_accepting
accept_list_item_start_note_multi_label
end
##
# Calls accept_list_item_start_number
def test_accept_list_item_start_number
@to.start_accepting
@to.accept_list_start @number_list
@to.accept_list_item_start @number_list.items.first
accept_list_item_start_number
end
##
# Calls accept_list_item_start_ualpha
def test_accept_list_item_start_ualpha
@to.start_accepting
@to.accept_list_start @ualpha_list
@to.accept_list_item_start @ualpha_list.items.first
accept_list_item_start_ualpha
end
##
# Calls accept_list_item_end_bullet
def test_accept_list_item_end_bullet
@to.start_accepting
@to.accept_list_start @bullet_list
@to.accept_list_item_start @bullet_list.items.first
@to.accept_list_item_end @bullet_list.items.first
accept_list_item_end_bullet
end
##
# Calls accept_list_item_end_label
def test_accept_list_item_end_label
@to.start_accepting
@to.accept_list_start @label_list
@to.accept_list_item_start @label_list.items.first
@to.accept_list_item_end @label_list.items.first
accept_list_item_end_label
end
##
# Calls accept_list_item_end_lalpha
def test_accept_list_item_end_lalpha
@to.start_accepting
@to.accept_list_start @lalpha_list
@to.accept_list_item_start @lalpha_list.items.first
@to.accept_list_item_end @lalpha_list.items.first
accept_list_item_end_lalpha
end
##
# Calls accept_list_item_end_note
def test_accept_list_item_end_note
@to.start_accepting
@to.accept_list_start @note_list
@to.accept_list_item_start @note_list.items.first
@to.accept_list_item_end @note_list.items.first
accept_list_item_end_note
end
##
# Calls accept_list_item_end_number
def test_accept_list_item_end_number
@to.start_accepting
@to.accept_list_start @number_list
@to.accept_list_item_start @number_list.items.first
@to.accept_list_item_end @number_list.items.first
accept_list_item_end_number
end
##
# Calls accept_list_item_end_ualpha
def test_accept_list_item_end_ualpha
@to.start_accepting
@to.accept_list_start @ualpha_list
@to.accept_list_item_start @ualpha_list.items.first
@to.accept_list_item_end @ualpha_list.items.first
accept_list_item_end_ualpha
end
##
# Calls accept_list_start_bullet
def test_accept_list_start_bullet
@to.start_accepting
@to.accept_list_start @bullet_list
accept_list_start_bullet
end
##
# Calls accept_list_start_label
def test_accept_list_start_label
@to.start_accepting
@to.accept_list_start @label_list
accept_list_start_label
end
##
# Calls accept_list_start_lalpha
def test_accept_list_start_lalpha
@to.start_accepting
@to.accept_list_start @lalpha_list
accept_list_start_lalpha
end
##
# Calls accept_list_start_note
def test_accept_list_start_note
@to.start_accepting
@to.accept_list_start @note_list
accept_list_start_note
end
##
# Calls accept_list_start_number
def test_accept_list_start_number
@to.start_accepting
@to.accept_list_start @number_list
accept_list_start_number
end
##
# Calls accept_list_start_ualpha
def test_accept_list_start_ualpha
@to.start_accepting
@to.accept_list_start @ualpha_list
accept_list_start_ualpha
end
##
# Calls accept_list_end_bullet
def test_accept_list_end_bullet
@to.start_accepting
@to.accept_list_start @bullet_list
@to.accept_list_end @bullet_list
accept_list_end_bullet
end
##
# Calls accept_list_end_label
def test_accept_list_end_label
@to.start_accepting
@to.accept_list_start @label_list
@to.accept_list_end @label_list
accept_list_end_label
end
##
# Calls accept_list_end_lalpha
def test_accept_list_end_lalpha
@to.start_accepting
@to.accept_list_start @lalpha_list
@to.accept_list_end @lalpha_list
accept_list_end_lalpha
end
##
# Calls accept_list_end_number
def test_accept_list_end_number
@to.start_accepting
@to.accept_list_start @number_list
@to.accept_list_end @number_list
accept_list_end_number
end
##
# Calls accept_list_end_note
def test_accept_list_end_note
@to.start_accepting
@to.accept_list_start @note_list
@to.accept_list_end @note_list
accept_list_end_note
end
##
# Calls accept_list_end_ualpha
def test_accept_list_end_ualpha
@to.start_accepting
@to.accept_list_start @ualpha_list
@to.accept_list_end @ualpha_list
accept_list_end_ualpha
end
##
# Calls list_nested with a two-level list
def test_list_nested
doc = @RM::Document.new(
@RM::List.new(:BULLET,
@RM::ListItem.new(nil,
@RM::Paragraph.new('l1'),
@RM::List.new(:BULLET,
@RM::ListItem.new(nil,
@RM::Paragraph.new('l1.1')))),
@RM::ListItem.new(nil,
@RM::Paragraph.new('l2'))))
doc.accept @to
list_nested
end
##
# Calls list_verbatim with a list containing a verbatim block
def test_list_verbatim # HACK overblown
doc =
doc(
list(:BULLET,
item(nil,
para('list stuff'),
blank_line,
verb("* list\n",
" with\n",
"\n",
" second\n",
"\n",
" 1. indented\n",
" 2. numbered\n",
"\n",
" third\n",
"\n",
"* second\n"))))
doc.accept @to
list_verbatim
end
end
end
end
PK ! coX X inline.rbnu [ warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w
PK ! | text_formatter_test_case.rbnu [ ##
# Test case for creating new plain-text RDoc::Markup formatters. See also
# RDoc::Markup::FormatterTestCase
#
# See test_rdoc_markup_to_rdoc.rb for a complete example.
#
# Example:
#
# class TestRDocMarkupToNewTextFormat < RDoc::Markup::TextFormatterTestCase
#
# add_visitor_tests
# add_text_tests
#
# def setup
# super
#
# @to = RDoc::Markup::ToNewTextFormat.new
# end
#
# def accept_blank_line
# assert_equal :junk, @to.res.join
# end
#
# # ...
#
# end
class RDoc::Markup::TextFormatterTestCase < RDoc::Markup::FormatterTestCase
##
# Adds test cases to the calling TestCase.
def self.add_text_tests
self.class_eval do
##
# Test case that calls @to.accept_heading
def test_accept_heading_indent
@to.start_accepting
@to.indent = 3
@to.accept_heading @RM::Heading.new(1, 'Hello')
accept_heading_indent
end
##
# Test case that calls @to.accept_rule
def test_accept_rule_indent
@to.start_accepting
@to.indent = 3
@to.accept_rule @RM::Rule.new(1)
accept_rule_indent
end
##
# Test case that calls @to.accept_verbatim
def test_accept_verbatim_indent
@to.start_accepting
@to.indent = 2
@to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
accept_verbatim_indent
end
##
# Test case that calls @to.accept_verbatim with a big indent
def test_accept_verbatim_big_indent
@to.start_accepting
@to.indent = 2
@to.accept_verbatim @RM::Verbatim.new("hi\n", "world\n")
accept_verbatim_big_indent
end
##
# Test case that calls @to.accept_paragraph with an indent
def test_accept_paragraph_indent
@to.start_accepting
@to.indent = 3
@to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
accept_paragraph_indent
end
##
# Test case that calls @to.accept_paragraph with a long line
def test_accept_paragraph_wrap
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
accept_paragraph_wrap
end
##
# Test case that calls @to.attributes with an escaped
# cross-reference. If this test doesn't pass something may be very
# wrong.
def test_attributes
assert_equal 'Dog', @to.attributes("\\Dog")
end
end
end
end
PK ! Gs s
special.rbnu [ ##
# Hold details of a special sequence
class RDoc::Markup::Special
##
# Special type
attr_reader :type
##
# Special text
attr_accessor :text
##
# Creates a new special sequence of +type+ with +text+
def initialize(type, text)
@type, @text = type, text
end
##
# Specials are equal when the have the same text and type
def ==(o)
self.text == o.text && self.type == o.type
end
def inspect # :nodoc:
"#" % [
object_id, @type, text.dump]
end
def to_s # :nodoc:
"Special: type=#{type} text=#{text.dump}"
end
end
PK ! 8TR8 R8 simple_markup.rbnu [ PK ! v? ? 8 sample/sample.rbnu [ PK ! ^ = sample/rdoc2latex.rbnu [ PK ! w4, , > test/TestParse.rbnu [ PK ! Z / / k test/AllTests.rbnu [ PK ! = cl test/TestInline.rbnu [ PK ! sp- - ŀ simple_markup/to_html.rbnu [ PK ! ) : simple_markup/fragments.rbnu [ PK ! Vڹ simple_markup/preprocess.rbnu [ PK ! 7^! ! simple_markup/to_latex.rbnu [ PK ! " " simple_markup/inline.rbnu [ PK ! k O simple_markup/lines.rbnu [ PK ! 8a U~ ~ simple_markup/to_flow.rbnu [ PK ! /
T# heading.rbnu [ PK ! fР
[) attributes.rbnu [ PK ! '>' ' 8. list.rbnu [ PK ! H2 2 5 to_joined_paragraph.rbnu [ PK ! w! !
< to_html.rbnu [ PK ! $AEb G^ to_bs.rbnu [ PK ! Up p
.e to_test.rbnu [ PK ! ?| | i table.rbnu [ PK ! X! n to_html_snippet.rbnu [ PK ! :|\l l ~ indented_paragraph.rbnu [ PK ! ,\ \ / to_markdown.rbnu [ PK ! 3ɀ
ɖ to_ansi.rbnu [ PK ! U to_label.rbnu [ PK ! i"
h include.rbnu [ PK ! DKz z document.rbnu [ PK ! 1q5 v list_item.rbnu [ PK ! XT\ n rule.rbnu [ PK ! \cb ¾ attr_changer.rbnu [ PK ! },$ $ formatter.rbnu [ PK ! z!!y y
to_rdoc.rbnu [ PK ! +
to_tt_only.rbnu [ PK ! 8 raw.rbnu [ PK ! G attribute_manager.rbnu [ PK ! ծA
hard_break.rbnu [ PK ! vai i
blank_line.rbnu [ PK ! 9 block_quote.rbnu [ PK ! >.> U to_table_of_contents.rbnu [ PK ! RR*/ / 5' to_html_crossref.rbnu [ PK ! u 8 verbatim.rbnu [ PK ! [Ba = regexp_handling.rbnu [ PK ! S: @ paragraph.rbnu [ PK ! aD C pre_process.rbnu [ PK ! !] c attr_span.rbnu [ PK ! 8 8 e parser.rbnu [ PK ! AWDaC aC Ȟ formatter_test_case.rbnu [ PK ! coX X o inline.rbnu [ PK ! | text_formatter_test_case.rbnu [ PK ! Gs s
< special.rbnu [ PK 3 3