summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/rdoc/markup
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rdoc/markup
Fresh start
Diffstat (limited to 'jni/ruby/lib/rdoc/markup')
-rw-r--r--jni/ruby/lib/rdoc/markup/attr_changer.rb22
-rw-r--r--jni/ruby/lib/rdoc/markup/attr_span.rb29
-rw-r--r--jni/ruby/lib/rdoc/markup/attribute_manager.rb343
-rw-r--r--jni/ruby/lib/rdoc/markup/attributes.rb70
-rw-r--r--jni/ruby/lib/rdoc/markup/blank_line.rb27
-rw-r--r--jni/ruby/lib/rdoc/markup/block_quote.rb14
-rw-r--r--jni/ruby/lib/rdoc/markup/document.rb164
-rw-r--r--jni/ruby/lib/rdoc/markup/formatter.rb264
-rw-r--r--jni/ruby/lib/rdoc/markup/formatter_test_case.rb767
-rw-r--r--jni/ruby/lib/rdoc/markup/hard_break.rb31
-rw-r--r--jni/ruby/lib/rdoc/markup/heading.rb78
-rw-r--r--jni/ruby/lib/rdoc/markup/include.rb42
-rw-r--r--jni/ruby/lib/rdoc/markup/indented_paragraph.rb47
-rw-r--r--jni/ruby/lib/rdoc/markup/inline.rb1
-rw-r--r--jni/ruby/lib/rdoc/markup/list.rb101
-rw-r--r--jni/ruby/lib/rdoc/markup/list_item.rb99
-rw-r--r--jni/ruby/lib/rdoc/markup/paragraph.rb28
-rw-r--r--jni/ruby/lib/rdoc/markup/parser.rb558
-rw-r--r--jni/ruby/lib/rdoc/markup/pre_process.rb293
-rw-r--r--jni/ruby/lib/rdoc/markup/raw.rb69
-rw-r--r--jni/ruby/lib/rdoc/markup/rule.rb20
-rw-r--r--jni/ruby/lib/rdoc/markup/special.rb40
-rw-r--r--jni/ruby/lib/rdoc/markup/text_formatter_test_case.rb114
-rw-r--r--jni/ruby/lib/rdoc/markup/to_ansi.rb93
-rw-r--r--jni/ruby/lib/rdoc/markup/to_bs.rb78
-rw-r--r--jni/ruby/lib/rdoc/markup/to_html.rb398
-rw-r--r--jni/ruby/lib/rdoc/markup/to_html_crossref.rb160
-rw-r--r--jni/ruby/lib/rdoc/markup/to_html_snippet.rb284
-rw-r--r--jni/ruby/lib/rdoc/markup/to_joined_paragraph.rb71
-rw-r--r--jni/ruby/lib/rdoc/markup/to_label.rb74
-rw-r--r--jni/ruby/lib/rdoc/markup/to_markdown.rb191
-rw-r--r--jni/ruby/lib/rdoc/markup/to_rdoc.rb333
-rw-r--r--jni/ruby/lib/rdoc/markup/to_table_of_contents.rb87
-rw-r--r--jni/ruby/lib/rdoc/markup/to_test.rb69
-rw-r--r--jni/ruby/lib/rdoc/markup/to_tt_only.rb120
-rw-r--r--jni/ruby/lib/rdoc/markup/verbatim.rb83
36 files changed, 5262 insertions, 0 deletions
diff --git a/jni/ruby/lib/rdoc/markup/attr_changer.rb b/jni/ruby/lib/rdoc/markup/attr_changer.rb
new file mode 100644
index 0000000..1772f18
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/attr_changer.rb
@@ -0,0 +1,22 @@
+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
+
diff --git a/jni/ruby/lib/rdoc/markup/attr_span.rb b/jni/ruby/lib/rdoc/markup/attr_span.rb
new file mode 100644
index 0000000..b5c1b3b
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/attr_span.rb
@@ -0,0 +1,29 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/attribute_manager.rb b/jni/ruby/lib/rdoc/markup/attribute_manager.rb
new file mode 100644
index 0000000..ce4ac76
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/attribute_manager.rb
@@ -0,0 +1,343 @@
+##
+# 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 <b>text</b>to the sequence
+ # \001\002<char>\001\003<char>, where <char> 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
+
diff --git a/jni/ruby/lib/rdoc/markup/attributes.rb b/jni/ruby/lib/rdoc/markup/attributes.rb
new file mode 100644
index 0000000..3423f10
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/attributes.rb
@@ -0,0 +1,70 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/blank_line.rb b/jni/ruby/lib/rdoc/markup/blank_line.rb
new file mode 100644
index 0000000..5da0ac8
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/blank_line.rb
@@ -0,0 +1,27 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/block_quote.rb b/jni/ruby/lib/rdoc/markup/block_quote.rb
new file mode 100644
index 0000000..552f0c4
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/block_quote.rb
@@ -0,0 +1,14 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/document.rb b/jni/ruby/lib/rdoc/markup/document.rb
new file mode 100644
index 0000000..be93d80
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/document.rb
@@ -0,0 +1,164 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/formatter.rb b/jni/ruby/lib/rdoc/markup/formatter.rb
new file mode 100644
index 0000000..7661c95
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/formatter.rb
@@ -0,0 +1,264 @@
+##
+# 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(/<br>/, :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 {<text>}[<url>] and <word>[<url>]
+
+ 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
+
diff --git a/jni/ruby/lib/rdoc/markup/formatter_test_case.rb b/jni/ruby/lib/rdoc/markup/formatter_test_case.rb
new file mode 100644
index 0000000..6616a75
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/formatter_test_case.rb
@@ -0,0 +1,767 @@
+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 <tt>@to</tt>.
+#
+# 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
+ # <tt>@to.end_accepting</tt> 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 <tt>@to.accept_document</tt>
+
+ 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 <b>bold words</b> reg')
+
+ accept_paragraph_b
+ end
+
+ ##
+ # Calls accept_paragraph_br with a RDoc::Markup::Paragraph containing
+ # a \<br>
+
+ def test_accept_paragraph_br
+ @to.start_accepting
+
+ @to.accept_paragraph para 'one<br>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 <em>italic words</em> 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("<table>",
+ "<tr><th>Name<th>Count",
+ "<tr><td>a<td>1",
+ "<tr><td>b<td>2",
+ "</table>")
+
+ 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('<tt>teletype</tt>',
+ 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
+
diff --git a/jni/ruby/lib/rdoc/markup/hard_break.rb b/jni/ruby/lib/rdoc/markup/hard_break.rb
new file mode 100644
index 0000000..8445ad2
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/hard_break.rb
@@ -0,0 +1,31 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/heading.rb b/jni/ruby/lib/rdoc/markup/heading.rb
new file mode 100644
index 0000000..535e310
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/heading.rb
@@ -0,0 +1,78 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/include.rb b/jni/ruby/lib/rdoc/markup/include.rb
new file mode 100644
index 0000000..a2e8903
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/include.rb
@@ -0,0 +1,42 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/indented_paragraph.rb b/jni/ruby/lib/rdoc/markup/indented_paragraph.rb
new file mode 100644
index 0000000..1b8a8c7
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/indented_paragraph.rb
@@ -0,0 +1,47 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/inline.rb b/jni/ruby/lib/rdoc/markup/inline.rb
new file mode 100644
index 0000000..fb3ab5c
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/inline.rb
@@ -0,0 +1 @@
+warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w
diff --git a/jni/ruby/lib/rdoc/markup/list.rb b/jni/ruby/lib/rdoc/markup/list.rb
new file mode 100644
index 0000000..89b7fc2
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/list.rb
@@ -0,0 +1,101 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/list_item.rb b/jni/ruby/lib/rdoc/markup/list_item.rb
new file mode 100644
index 0000000..c5e59fe
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/list_item.rb
@@ -0,0 +1,99 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/paragraph.rb b/jni/ruby/lib/rdoc/markup/paragraph.rb
new file mode 100644
index 0000000..7180729
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/paragraph.rb
@@ -0,0 +1,28 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/parser.rb b/jni/ruby/lib/rdoc/markup/parser.rb
new file mode 100644
index 0000000..cc828a4
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/parser.rb
@@ -0,0 +1,558 @@
+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
+# <tt>\+blah\+</tt> 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
+
diff --git a/jni/ruby/lib/rdoc/markup/pre_process.rb b/jni/ruby/lib/rdoc/markup/pre_process.rb
new file mode 100644
index 0000000..01fb293
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/pre_process.rb
@@ -0,0 +1,293 @@
+##
+# 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 <tt>include</tt> 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 <tt>:include: _filename_</tt> 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 <tt>:include:</tt> 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
+
diff --git a/jni/ruby/lib/rdoc/markup/raw.rb b/jni/ruby/lib/rdoc/markup/raw.rb
new file mode 100644
index 0000000..e11e8ef
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/raw.rb
@@ -0,0 +1,69 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/rule.rb b/jni/ruby/lib/rdoc/markup/rule.rb
new file mode 100644
index 0000000..b778f2b
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/rule.rb
@@ -0,0 +1,20 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/special.rb b/jni/ruby/lib/rdoc/markup/special.rb
new file mode 100644
index 0000000..1c0fc03
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/special.rb
@@ -0,0 +1,40 @@
+##
+# 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:
+ "#<RDoc::Markup::Special:0x%x @type=%p, @text=%p>" % [
+ object_id, @type, text.dump]
+ end
+
+ def to_s # :nodoc:
+ "Special: type=#{type} text=#{text.dump}"
+ end
+
+end
+
diff --git a/jni/ruby/lib/rdoc/markup/text_formatter_test_case.rb b/jni/ruby/lib/rdoc/markup/text_formatter_test_case.rb
new file mode 100644
index 0000000..4abf425
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/text_formatter_test_case.rb
@@ -0,0 +1,114 @@
+##
+# 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 <tt>@to.accept_heading</tt>
+
+ 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 <tt>@to.accept_rule</tt>
+
+ 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 <tt>@to.accept_verbatim</tt>
+
+ 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 <tt>@to.accept_verbatim</tt> 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 <tt>@to.accept_paragraph</tt> 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 <tt>@to.accept_paragraph</tt> 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 <tt>@to.attributes</tt> 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_ansi.rb b/jni/ruby/lib/rdoc/markup/to_ansi.rb
new file mode 100644
index 0000000..4d439ce
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_ansi.rb
@@ -0,0 +1,93 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_bs.rb b/jni/ruby/lib/rdoc/markup/to_bs.rb
new file mode 100644
index 0000000..10c3185
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_bs.rb
@@ -0,0 +1,78 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_html.rb b/jni/ruby/lib/rdoc/markup/to_html.rb
new file mode 100644
index 0000000..2b1216e
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_html.rb
@@ -0,0 +1,398 @@
+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 => ['<ul>', '</ul>'],
+ :LABEL => ['<dl class="rdoc-list label-list">', '</dl>'],
+ :LALPHA => ['<ol style="list-style-type: lower-alpha">', '</ol>'],
+ :NOTE => ['<dl class="rdoc-list note-list">', '</dl>'],
+ :NUMBER => ['<ol>', '</ol>'],
+ :UALPHA => ['<ol style="list-style-type: upper-alpha">', '</ol>'],
+ }
+
+ 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 = "<br>\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:/
+ "<img src=\"#{$'}\">"
+ else
+ url =~ /\Ardoc-[a-z]+:/
+
+ $'
+ end
+ end
+
+ ##
+ # +special+ is a <code><br></code>
+
+ def handle_special_HARD_BREAK special
+ '<br>'
+ end
+
+ ##
+ # +special+ is a potential link. The following schemes are handled:
+ #
+ # <tt>mailto:</tt>::
+ # Inserted as-is.
+ # <tt>http:</tt>::
+ # Links are checked to see if they reference an image. If so, that image
+ # gets inserted using an <tt><img></tt> tag. Otherwise a conventional
+ # <tt><a href></tt> is used.
+ # <tt>link:</tt>::
+ # 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
+ # <tt>label[url]</tt> or <tt>{long label}[url]</tt>
+
+ 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<blockquote>"
+
+ block_quote.parts.each do |part|
+ part.accept self
+ end
+
+ @res << "</blockquote>\n"
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_paragraph paragraph
+ @res << "\n<p>"
+ text = paragraph.text @hard_break
+ text = text.gsub(/\r?\n/, ' ')
+ @res << wrap(to_html(text))
+ @res << "</p>\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<pre><code>#{CGI.escapeHTML text}</code></pre>\n"
+ else
+ @res << "\n<pre#{klass}>#{content}</pre>\n"
+ end
+ end
+
+ ##
+ # Adds +rule+ to the output
+
+ def accept_rule rule
+ @res << "<hr>\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("<p />") << "\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<h#{level} id=\"#{label}\">"
+ else
+ "\n<h#{level}>"
+ end
+ @res << to_html(heading.text)
+ unless @options.pipe then
+ @res << "<span><a href=\"##{label}\">&para;</a>"
+ @res << " <a href=\"#top\">&uarr;</a></span>"
+ end
+ @res << "</h#{level}>\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
+ "<img src=\"#{url}\" />"
+ else
+ text = text.sub %r%^#{scheme}:/*%i, ''
+ text = text.sub %r%^[*\^](\d+)$%, '\1'
+
+ link = "<a#{id} href=\"#{url}\">#{text}</a>"
+
+ link = "<sup>#{link}</sup>" 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, "<strong>", "</strong>"
+ add_tag :TT, "<code>", "</code>"
+ add_tag :EM, "<em>", "</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
+ "<li>"
+ when :LABEL, :NOTE then
+ Array(list_item.label).map do |label|
+ "<dt>#{to_html label}\n"
+ end.join << "<dd>"
+ 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
+ "</li>"
+ when :LABEL, :NOTE then
+ "</dd>"
+ else
+ raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
+ end
+ end
+
+ ##
+ # Returns true if text is valid ruby syntax
+
+ def parseable? text
+ eval("BEGIN {return true}\n#{text}")
+ rescue SyntaxError
+ false
+ end
+
+ ##
+ # Converts +item+ to HTML using RDoc::Text#to_html
+
+ def to_html item
+ super convert_flow @am.flow item
+ end
+
+end
+
diff --git a/jni/ruby/lib/rdoc/markup/to_html_crossref.rb b/jni/ruby/lib/rdoc/markup/to_html_crossref.rb
new file mode 100644
index 0000000..d27e0ab
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_html_crossref.rb
@@ -0,0 +1,160 @@
+##
+# 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 <tt>RDoc::Markup::</tt> 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 <tt>rdoc-ref:</tt> 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 <tt>rdoc-ref:</tt> 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
+
+ "<a href=\"#{path}\">#{text}</a>"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rdoc/markup/to_html_snippet.rb b/jni/ruby/lib/rdoc/markup/to_html_snippet.rb
new file mode 100644
index 0000000..4ad0a9a
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_html_snippet.rb
@@ -0,0 +1,284 @@
+##
+# 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 << "<p>#{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 || "<p>"
+
+ 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 <code><br></code>
+
+ def handle_special_HARD_BREAK special
+ @characters -= 4
+ '<br>'
+ 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
+ "<p>"
+ when :LABEL, :NOTE then
+ labels = Array(list_item.label).map do |label|
+ to_html label
+ end.join ', '
+
+ labels << " &mdash; " unless labels.empty?
+
+ start = "<p>#{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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_joined_paragraph.rb b/jni/ruby/lib/rdoc/markup/to_joined_paragraph.rb
new file mode 100644
index 0000000..8358410
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_joined_paragraph.rb
@@ -0,0 +1,71 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_label.rb b/jni/ruby/lib/rdoc/markup/to_label.rb
new file mode 100644
index 0000000..6fbe4a3
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_label.rb
@@ -0,0 +1,74 @@
+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).gsub('%', '-').sub(/^-/, '')
+ 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_markdown.rb b/jni/ruby/lib/rdoc/markup/to_markdown.rb
new file mode 100644
index 0000000..d4b15bf
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_markdown.rb
@@ -0,0 +1,191 @@
+# :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 <tt>rdoc-</tt> 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_rdoc.rb b/jni/ruby/lib/rdoc/markup/to_rdoc.rb
new file mode 100644
index 0000000..f16b4ed
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_rdoc.rb
@@ -0,0 +1,333 @@
+##
+# 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, "<b>", "</b>"
+ add_tag :TT, "<tt>", "</tt>"
+ add_tag :EM, "<em>", "</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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_table_of_contents.rb b/jni/ruby/lib/rdoc/markup/to_table_of_contents.rb
new file mode 100644
index 0000000..2e0f98c
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_table_of_contents.rb
@@ -0,0 +1,87 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_test.rb b/jni/ruby/lib/rdoc/markup/to_test.rb
new file mode 100644
index 0000000..c51f64b
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_test.rb
@@ -0,0 +1,69 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/to_tt_only.rb b/jni/ruby/lib/rdoc/markup/to_tt_only.rb
new file mode 100644
index 0000000..e2da20c
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/to_tt_only.rb
@@ -0,0 +1,120 @@
+##
+# 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
+
diff --git a/jni/ruby/lib/rdoc/markup/verbatim.rb b/jni/ruby/lib/rdoc/markup/verbatim.rb
new file mode 100644
index 0000000..0ddde67
--- /dev/null
+++ b/jni/ruby/lib/rdoc/markup/verbatim.rb
@@ -0,0 +1,83 @@
+##
+# 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
+