aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rdoc/markup
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
commit2ef9c50c6e405717d06362787c4549ca4f1c6485 (patch)
treeee99486567461dd5796f3d6edcc9e204187f2666 /lib/rdoc/markup
parentd7effd506f5b91a636f2e6452ef1946b923007c7 (diff)
downloadruby-2ef9c50c6e405717d06362787c4549ca4f1c6485.tar.gz
Import RDoc 3
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30249 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rdoc/markup')
-rw-r--r--lib/rdoc/markup/attribute_manager.rb33
-rw-r--r--lib/rdoc/markup/blank_line.rb14
-rw-r--r--lib/rdoc/markup/document.rb6
-rw-r--r--lib/rdoc/markup/formatter.rb10
-rw-r--r--lib/rdoc/markup/formatter_test_case.rb342
-rw-r--r--lib/rdoc/markup/heading.rb3
-rw-r--r--lib/rdoc/markup/inline.rb12
-rw-r--r--lib/rdoc/markup/list.rb3
-rw-r--r--lib/rdoc/markup/list_item.rb3
-rw-r--r--lib/rdoc/markup/paragraph.rb3
-rw-r--r--lib/rdoc/markup/parser.rb426
-rw-r--r--lib/rdoc/markup/pre_process.rb (renamed from lib/rdoc/markup/preprocess.rb)79
-rw-r--r--lib/rdoc/markup/raw.rb4
-rw-r--r--lib/rdoc/markup/rule.rb3
-rw-r--r--lib/rdoc/markup/text_formatter_test_case.rb116
-rw-r--r--lib/rdoc/markup/to_ansi.rb16
-rw-r--r--lib/rdoc/markup/to_bs.rb10
-rw-r--r--lib/rdoc/markup/to_html.rb175
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb109
-rw-r--r--lib/rdoc/markup/to_rdoc.rb143
-rw-r--r--lib/rdoc/markup/to_test.rb10
-rw-r--r--lib/rdoc/markup/verbatim.rb9
22 files changed, 1075 insertions, 454 deletions
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index e86e7f6812..2ee243ab0b 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -15,9 +15,12 @@ class RDoc::Markup::AttributeManager
# optimistic
#++
- A_PROTECT = 004 # :nodoc:
+ A_PROTECT = 004 # :nodoc:
- PROTECT_ATTR = A_PROTECT.chr # :nodoc:
+ ##
+ # Special mask character to prevent inline markup handling
+
+ PROTECT_ATTR = A_PROTECT.chr # :nodoc:
##
# This maps delimiters that occur around words (such as *bold* or +tt+)
@@ -56,7 +59,7 @@ class RDoc::Markup::AttributeManager
def initialize
@html_tags = {}
@matching_word_pairs = {}
- @protectable = %w[<\\]
+ @protectable = %w[<]
@special = {}
@word_pair_map = {}
@@ -79,12 +82,19 @@ class RDoc::Markup::AttributeManager
RDoc::Markup::AttrChanger.new turn_on, turn_off
end
- def change_attribute(current, new)
+ ##
+ # Changes the current attribute from +current+ to +new+
+
+ def change_attribute current, new
diff = current ^ new
attribute(new & diff, current & diff)
end
- def changed_attribute_by_name(current_set, new_set)
+ ##
+ # 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 |= RDoc::Markup::Attribute.bitmap_for(name)
@@ -97,6 +107,9 @@ class RDoc::Markup::AttributeManager
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/, '')
@@ -112,7 +125,7 @@ class RDoc::Markup::AttributeManager
# first do matching ones
tags = @matching_word_pairs.keys.join("")
- re = /(^|[^\w#{NULL}])([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
+ re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
1 while str.gsub!(re) do
attr = @matching_word_pairs[$2]
@@ -164,6 +177,9 @@ class RDoc::Markup::AttributeManager
# 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!(/\\([#{Regexp.escape @protectable.join('')}])/,
"\\1#{PROTECT_ATTR}")
end
@@ -228,8 +244,8 @@ class RDoc::Markup::AttributeManager
@attrs = RDoc::Markup::AttrSpan.new @str.length
- convert_html @str, @attrs
convert_attrs @str, @attrs
+ convert_html @str, @attrs
convert_specials @str, @attrs
unmask_protected_sequences
@@ -262,6 +278,9 @@ class RDoc::Markup::AttributeManager
end
end
+ ##
+ # Splits the string into chunks by attribute change
+
def split_into_flow
res = []
current_attr = 0
diff --git a/lib/rdoc/markup/blank_line.rb b/lib/rdoc/markup/blank_line.rb
index a8c07c8e57..5da0ac8d81 100644
--- a/lib/rdoc/markup/blank_line.rb
+++ b/lib/rdoc/markup/blank_line.rb
@@ -1,12 +1,20 @@
##
-# An empty line
+# An empty line. This class is a singleton.
class RDoc::Markup::BlankLine
- def == other # :nodoc:
- self.class == other.class
+ @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
diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb
index 7963e9afe1..688e8e822e 100644
--- a/lib/rdoc/markup/document.rb
+++ b/lib/rdoc/markup/document.rb
@@ -39,6 +39,9 @@ class RDoc::Markup::Document
self.class == other.class and @parts == other.parts
end
+ ##
+ # Runs this document and all its #items through +visitor+
+
def accept visitor
visitor.start_accepting
@@ -49,6 +52,9 @@ class RDoc::Markup::Document
visitor.end_accepting
end
+ ##
+ # Does this document have no parts?
+
def empty?
@parts.empty?
end
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
index 993e523f0c..9308954de1 100644
--- a/lib/rdoc/markup/formatter.rb
+++ b/lib/rdoc/markup/formatter.rb
@@ -7,6 +7,10 @@ require 'rdoc/markup'
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)
##
@@ -101,6 +105,9 @@ class RDoc::Markup::Formatter
@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?
@@ -113,6 +120,9 @@ class RDoc::Markup::Formatter
end
end
+ ##
+ # Turns off tags for +item+ on +res+
+
def off_tags res, item
attr_mask = item.turn_off
return if attr_mask.zero?
diff --git a/lib/rdoc/markup/formatter_test_case.rb b/lib/rdoc/markup/formatter_test_case.rb
index 26c8d63332..dd755c55d1 100644
--- a/lib/rdoc/markup/formatter_test_case.rb
+++ b/lib/rdoc/markup/formatter_test_case.rb
@@ -4,14 +4,57 @@ require 'rdoc/markup/formatter'
##
# 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 < MiniTest::Unit::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
@m = RDoc::Markup.new
- @am = RDoc::Markup::AttributeManager.new
@RM = RDoc::Markup
@bullet_list = @RM::List.new(:BULLET,
@@ -39,14 +82,25 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
end
+ ##
+ # Call to add the visitor tests to your test case
+
def self.add_visitor_tests
self.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'
@@ -54,6 +108,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
end_accepting
end
+ ##
+ # Calls accept_blank_line
+
def test_accept_blank_line
@to.start_accepting
@@ -62,6 +119,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_blank_line
end
+ ##
+ # Calls accept_heading with a level 5 RDoc::Markup::Heading
+
def test_accept_heading
@to.start_accepting
@@ -70,6 +130,79 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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
@@ -78,15 +211,80 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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_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")
+ @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
@@ -99,6 +297,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_raw
end
+ ##
+ # Calls accept_rule with a RDoc::Markup::Rule
+
def test_accept_rule
@to.start_accepting
@@ -107,6 +308,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_rule
end
+ ##
+ # Calls accept_list_item_start_bullet
+
def test_accept_list_item_start_bullet
@to.start_accepting
@@ -117,6 +321,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_bullet
end
+ ##
+ # Calls accept_list_item_start_label
+
def test_accept_list_item_start_label
@to.start_accepting
@@ -127,6 +334,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_label
end
+ ##
+ # Calls accept_list_item_start_lalpha
+
def test_accept_list_item_start_lalpha
@to.start_accepting
@@ -137,6 +347,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_lalpha
end
+ ##
+ # Calls accept_list_item_start_note
+
def test_accept_list_item_start_note
@to.start_accepting
@@ -147,6 +360,26 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_note
end
+ ##
+ # Calls accept_list_item_start_note_2
+
+ def test_accept_list_item_start_note_2
+ list = @RM::List.new(:NOTE,
+ @RM::ListItem.new('<tt>teletype</tt>',
+ @RM::Paragraph.new('teletype description')))
+
+ @to.start_accepting
+
+ list.accept @to
+
+ @to.end_accepting
+
+ accept_list_item_start_note_2
+ end
+
+ ##
+ # Calls accept_list_item_start_number
+
def test_accept_list_item_start_number
@to.start_accepting
@@ -157,6 +390,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_number
end
+ ##
+ # Calls accept_list_item_start_ualpha
+
def test_accept_list_item_start_ualpha
@to.start_accepting
@@ -167,6 +403,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_ualpha
end
+ ##
+ # Calls accept_list_item_end_bullet
+
def test_accept_list_item_end_bullet
@to.start_accepting
@@ -179,6 +418,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_bullet
end
+ ##
+ # Calls accept_list_item_end_label
+
def test_accept_list_item_end_label
@to.start_accepting
@@ -191,6 +433,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_label
end
+ ##
+ # Calls accept_list_item_end_lalpha
+
def test_accept_list_item_end_lalpha
@to.start_accepting
@@ -203,6 +448,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_lalpha
end
+ ##
+ # Calls accept_list_item_end_note
+
def test_accept_list_item_end_note
@to.start_accepting
@@ -215,6 +463,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_note
end
+ ##
+ # Calls accept_list_item_end_number
+
def test_accept_list_item_end_number
@to.start_accepting
@@ -227,6 +478,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_number
end
+ ##
+ # Calls accept_list_item_end_ualpha
+
def test_accept_list_item_end_ualpha
@to.start_accepting
@@ -239,6 +493,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_ualpha
end
+ ##
+ # Calls accept_list_start_bullet
+
def test_accept_list_start_bullet
@to.start_accepting
@@ -247,6 +504,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_bullet
end
+ ##
+ # Calls accept_list_start_label
+
def test_accept_list_start_label
@to.start_accepting
@@ -255,6 +515,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_label
end
+ ##
+ # Calls accept_list_start_lalpha
+
def test_accept_list_start_lalpha
@to.start_accepting
@@ -263,6 +526,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_lalpha
end
+ ##
+ # Calls accept_list_start_note
+
def test_accept_list_start_note
@to.start_accepting
@@ -271,6 +537,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_note
end
+ ##
+ # Calls accept_list_start_number
+
def test_accept_list_start_number
@to.start_accepting
@@ -279,6 +548,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_number
end
+ ##
+ # Calls accept_list_start_ualpha
+
def test_accept_list_start_ualpha
@to.start_accepting
@@ -287,6 +559,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_ualpha
end
+ ##
+ # Calls accept_list_end_bullet
+
def test_accept_list_end_bullet
@to.start_accepting
@@ -297,6 +572,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_bullet
end
+ ##
+ # Calls accept_list_end_label
+
def test_accept_list_end_label
@to.start_accepting
@@ -307,6 +585,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_label
end
+ ##
+ # Calls accept_list_end_lalpha
+
def test_accept_list_end_lalpha
@to.start_accepting
@@ -317,6 +598,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_lalpha
end
+ ##
+ # Calls accept_list_end_number
+
def test_accept_list_end_number
@to.start_accepting
@@ -327,6 +611,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_number
end
+ ##
+ # Calls accept_list_end_note
+
def test_accept_list_end_note
@to.start_accepting
@@ -337,6 +624,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_note
end
+ ##
+ # Calls accept_list_end_ulpha
+
def test_accept_list_end_ualpha
@to.start_accepting
@@ -346,6 +636,52 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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 = @RM::Document.new(
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('list', 'stuff'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("* 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
diff --git a/lib/rdoc/markup/heading.rb b/lib/rdoc/markup/heading.rb
index 21e2574d68..081d637729 100644
--- a/lib/rdoc/markup/heading.rb
+++ b/lib/rdoc/markup/heading.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Heading < Struct.new :level, :text
+ ##
+ # Calls #accept_heading on +wisitor+
+
def accept visitor
visitor.accept_heading self
end
diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb
index 1b5eac45ae..f5bf98a071 100644
--- a/lib/rdoc/markup/inline.rb
+++ b/lib/rdoc/markup/inline.rb
@@ -1,3 +1,4 @@
+require 'rdoc'
class RDoc::Markup
##
@@ -14,6 +15,9 @@ class RDoc::Markup
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
@@next_bitmap = 2
+ ##
+ # Returns a unique bit for +name+
+
def self.bitmap_for(name)
bitmap = @@name_to_bitmap[name]
unless bitmap then
@@ -24,6 +28,9 @@ class RDoc::Markup
bitmap
end
+ ##
+ # Returns a string reperesentation of +bitmap+
+
def self.as_string(bitmap)
return "none" if bitmap.zero?
res = []
@@ -33,6 +40,9 @@ class RDoc::Markup
res.join(",")
end
+ ##
+ # yields each attribute name in +bitmap+
+
def self.each_name_of(bitmap)
@@name_to_bitmap.each do |name, bit|
next if bit == SPECIAL
@@ -75,7 +85,7 @@ class RDoc::Markup
end
##
- # Acccesses flags for character +n+
+ # Accesses flags for character +n+
def [](n)
@attrs[n]
diff --git a/lib/rdoc/markup/list.rb b/lib/rdoc/markup/list.rb
index 75326ed836..820b4c9645 100644
--- a/lib/rdoc/markup/list.rb
+++ b/lib/rdoc/markup/list.rb
@@ -35,6 +35,9 @@ class RDoc::Markup::List
@items == other.items
end
+ ##
+ # Runs this list and all its #items through +visitor+
+
def accept visitor
visitor.accept_list_start self
diff --git a/lib/rdoc/markup/list_item.rb b/lib/rdoc/markup/list_item.rb
index 500e814fe1..d719c352ec 100644
--- a/lib/rdoc/markup/list_item.rb
+++ b/lib/rdoc/markup/list_item.rb
@@ -35,6 +35,9 @@ class RDoc::Markup::ListItem
@parts == other.parts
end
+ ##
+ # Runs this list item and all its #parts through +visitor+
+
def accept visitor
visitor.accept_list_item_start self
diff --git a/lib/rdoc/markup/paragraph.rb b/lib/rdoc/markup/paragraph.rb
index a9923ed24d..808430d576 100644
--- a/lib/rdoc/markup/paragraph.rb
+++ b/lib/rdoc/markup/paragraph.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Paragraph < RDoc::Markup::Raw
+ ##
+ # Calls #accept_paragraph on +visitor+
+
def accept visitor
visitor.accept_paragraph self
end
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 9fba69dc29..ea02ee3c5b 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -52,13 +52,13 @@ class RDoc::Markup::Parser
attr_reader :tokens
##
- # Parsers +str+ into a Document
+ # Parses +str+ into a Document
def self.parse str
parser = new
- #parser.debug = true
parser.tokenize str
- RDoc::Markup::Document.new(*parser.parse)
+ doc = RDoc::Markup::Document.new
+ parser.parse doc
end
##
@@ -86,6 +86,7 @@ class RDoc::Markup::Parser
# Builds a Heading of +level+
def build_heading level
+ _, text, = get # TEXT
heading = RDoc::Markup::Heading.new level, text
skip :NEWLINE
@@ -105,38 +106,69 @@ class RDoc::Markup::Parser
case type
when :BULLET, :LABEL, :LALPHA, :NOTE, :NUMBER, :UALPHA then
- list_type = type
- if column < margin then
+ if column < margin || (list.type && list.type != type) then
unget
break
end
- if list.type and list.type != list_type then
- unget
- break
- end
-
- list.type = list_type
+ list.type = type
+ peek_type, _, column, = peek_token
case type
when :NOTE, :LABEL then
- _, indent, = get # SPACE
- if :NEWLINE == peek_token.first then
- get
- peek_type, new_indent, peek_column, = peek_token
- indent = new_indent if
- peek_type == :INDENT and peek_column >= column
- unget
+ 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 = 1
+ elsif column == margin then
+ case peek_type
+ when type
+ empty = 2 # continue
+ when *LIST_TOKENS
+ empty = 1
+ else
+ empty = 0
+ end
+ else
+ empty = 0
+ end
+
+ if empty > 0 then
+ item = RDoc::Markup::ListItem.new(data)
+ item << RDoc::Markup::BlankLine.new
+ list << item
+ break if empty == 1
+ next
+ end
end
else
data = nil
- _, indent, = get
end
- list_item = build_list_item(margin + indent, data)
+ list_item = RDoc::Markup::ListItem.new data
+ parse list_item, column
+ list << list_item
- list << list_item if list_item
else
unget
break
@@ -151,54 +183,6 @@ class RDoc::Markup::Parser
end
##
- # Builds a ListItem that is flush to +indent+ with type +item_type+
-
- def build_list_item indent, item_type = nil
- p :list_item_start => [indent, item_type] if @debug
-
- list_item = RDoc::Markup::ListItem.new item_type
-
- until @tokens.empty? do
- type, data, column = get
-
- if column < indent and
- not type == :NEWLINE and
- (type != :INDENT or data < indent) then
- unget
- break
- end
-
- case type
- when :INDENT then
- unget
- list_item.push(*parse(indent))
- when :TEXT then
- unget
- list_item << build_paragraph(indent)
- when :HEADER then
- list_item << build_heading(data)
- when :NEWLINE then
- list_item << RDoc::Markup::BlankLine.new
- when *LIST_TOKENS then
- unget
- list_item << build_list(column)
- else
- raise ParseError, "Unhandled token #{@current_token.inspect}"
- end
- end
-
- p :list_item_end => [indent, item_type] if @debug
-
- return nil if list_item.empty?
-
- list_item.parts.shift if
- RDoc::Markup::BlankLine === list_item.parts.first and
- list_item.length > 1
-
- list_item
- end
-
- ##
# Builds a Paragraph that is flush to +margin+
def build_paragraph margin
@@ -209,18 +193,7 @@ class RDoc::Markup::Parser
until @tokens.empty? do
type, data, column, = get
- case type
- when :INDENT then
- next if data == margin and peek_token[0] == :TEXT
-
- unget
- break
- when :TEXT then
- if column != margin then
- unget
- break
- end
-
+ if type == :TEXT && column == margin then
paragraph << data
skip :NEWLINE
else
@@ -235,67 +208,81 @@ class RDoc::Markup::Parser
end
##
- # Builds a Verbatim that is flush to +margin+
+ # 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
- case type
- when :INDENT then
- if margin >= data then
- unget
- break
- end
+ if type == :NEWLINE then
+ line << data
+ verbatim << line
+ line = ''
+ generate_leading_spaces = true
+ next
+ end
- indent = data - margin
+ if column <= margin
+ unget
+ break
+ end
- verbatim << ' ' * indent
- when :HEADER then
- verbatim << '=' * data
+ 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
- verbatim << ' ' * (peek_column - column - data)
+ indent = peek_column - column - data
+ line << ' ' * indent
when :RULE then
width = 2 + data
- verbatim << '-' * width
-
+ line << '-' * width
_, _, peek_column, = peek_token
- peek_column ||= column + data + 2
- verbatim << ' ' * (peek_column - column - width)
+ peek_column ||= column + width
+ indent = peek_column - column - width
+ line << ' ' * indent
when :TEXT then
- verbatim << data
- when *LIST_TOKENS then
- if column <= margin then
- unget
- break
- end
-
+ line << data
+ else # *LIST_TOKENS
list_marker = case type
- when :BULLET then '*'
- when :LABEL then "[#{data}]"
- when :LALPHA, :NUMBER, :UALPHA then "#{data}."
- when :NOTE then "#{data}::"
+ when :BULLET then data
+ when :LABEL then "[#{data}]"
+ when :NOTE then "#{data}::"
+ else # :LALPHA, :NUMBER, :UALPHA
+ "#{data}."
end
-
- verbatim << list_marker
-
- _, data, = get
-
- verbatim << ' ' * (data - list_marker.length)
- when :NEWLINE then
- verbatim << data
- break unless [:INDENT, :NEWLINE].include? peek_token[0]
- else
- unget
- break
+ 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
@@ -313,65 +300,60 @@ class RDoc::Markup::Parser
end
##
- # Parses the tokens into a Document
-
- def parse indent = 0
+ # 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
- document = []
-
until @tokens.empty? do
type, data, column, = get
- if type != :INDENT and column < indent then
+ if type == :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 verbattim
+ 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
- document << build_heading(data)
- when :INDENT then
- if indent > data then
- unget
- break
- elsif indent == data then
- next
- end
-
- unget
- document << build_verbatim(indent)
- when :NEWLINE then
- document << RDoc::Markup::BlankLine.new
- skip :NEWLINE, false
+ parent << build_heading(data)
when :RULE then
- document << RDoc::Markup::Rule.new(data)
+ parent << RDoc::Markup::Rule.new(data)
skip :NEWLINE
when :TEXT then
unget
- document << build_paragraph(indent)
-
- # we're done with this paragraph (indent mismatch)
- break if peek_token[0] == :TEXT
+ parent << build_paragraph(indent)
when *LIST_TOKENS then
unget
-
- list = build_list(indent)
-
- document << list if list
-
- # we're done with this list (indent mismatch)
- break if LIST_TOKENS.include? peek_token.first and indent > 0
+ parent << build_list(indent)
else
type, data, column, line = @current_token
- raise ParseError,
- "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
+ raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
end
end
p :parse_end => indent if @debug
- document
+ parent
+
end
##
@@ -384,63 +366,16 @@ class RDoc::Markup::Parser
end
##
- # Skips a token of +token_type+, optionally raising an error.
+ # 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
-
- ##
- # Consumes tokens until NEWLINE and turns them back into text
-
- def text
- text = ''
-
- loop do
- type, data, = get
-
- text << case type
- when :BULLET then
- _, space, = get # SPACE
- "*#{' ' * (space - 1)}"
- when :LABEL then
- _, space, = get # SPACE
- "[#{data}]#{' ' * (space - data.length - 2)}"
- when :LALPHA, :NUMBER, :UALPHA then
- _, space, = get # SPACE
- "#{data}.#{' ' * (space - 2)}"
- when :NOTE then
- _, space = get # SPACE
- "#{data}::#{' ' * (space - data.length - 2)}"
- when :TEXT then
- data
- when :NEWLINE then
- unget
- break
- when nil then
- break
- else
- raise ParseError, "unhandled token #{@current_token.inspect}"
- end
- end
-
- text
- end
-
- ##
- # Calculates the column and line of the current token based on +offset+.
-
- def token_pos offset
- [offset - @line_pos, @line]
+ raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
end
##
@@ -455,51 +390,62 @@ class RDoc::Markup::Parser
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 = s.pos
@line += 1
token
- when s.scan(/ +/) then
- [:INDENT, s.matched_size, *token_pos(pos)]
+ # === text => :HEADER then :TEXT
when s.scan(/(=+)\s*/) then
level = s[1].length
level = 6 if level > 6
@tokens << [:HEADER, level, *token_pos(pos)]
-
pos = s.pos
s.scan(/.*/)
- [:TEXT, s.matched, *token_pos(pos)]
- when s.scan(/^(-{3,}) *$/) then
+ [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
+ # --- (at least 3) and nothing else on the line => :RULE
+ when s.scan(/(-{3,}) *$/) then
[:RULE, s[1].length - 2, *token_pos(pos)]
- when s.scan(/([*-])\s+/) then
- @tokens << [:BULLET, :BULLET, *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
- when s.scan(/([a-z]|\d+)\.[ \t]+\S/i) then
+ # * 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]
- width = s.matched_size - 1
-
- s.pos -= 1 # 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
-
- @tokens << [list_type, list_label, *token_pos(pos)]
- [:SPACE, width, *token_pos(pos)]
+ 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(/\[(.*?)\]( +|$)/) then
- @tokens << [:LABEL, s[1], *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
+ [:LABEL, s[1], *token_pos(pos)]
+ # text:: followed by spaces or end of line => :NOTE
when s.scan(/(.*?)::( +|$)/) then
- @tokens << [:NOTE, s[1], *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
+ [:NOTE, s[1], *token_pos(pos)]
+ # anything else: :TEXT
else s.scan(/.*/)
- [:TEXT, s.matched, *token_pos(pos)]
+ [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
end
end
@@ -507,9 +453,17 @@ class RDoc::Markup::Parser
end
##
- # Returns the current token or +token+ to the token stream
+ # Calculates the column and line of the current token based on +offset+.
+
+ def token_pos offset
+ [offset - @line_pos, @line]
+ end
+
+ ##
+ # Returns the current token to the token stream
- def unget token = @current_token
+ def unget
+ token = @current_token
p :unget => token if @debug
raise Error, 'too many #ungets' if token == @tokens.first
@tokens.unshift token if token
diff --git a/lib/rdoc/markup/preprocess.rb b/lib/rdoc/markup/pre_process.rb
index cefb498916..e59bd227b7 100644
--- a/lib/rdoc/markup/preprocess.rb
+++ b/lib/rdoc/markup/pre_process.rb
@@ -1,12 +1,15 @@
require 'rdoc/markup'
+require 'rdoc/encoding'
##
# Handle common directives that can occur in a block of text:
#
-# : include : filename
+# \:include: filename
#
-# RDoc plugin authors can register additional directives to be handled through
-# RDoc::Markup::PreProcess::register
+# 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
class RDoc::Markup::PreProcess
@@ -52,18 +55,25 @@ class RDoc::Markup::PreProcess
# +code_object+. See RDoc::CodeObject#metadata
def handle text, code_object = nil
- text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do
- next $& if $3.empty? and $4 and $4[0, 1] == ':'
+ # regexp helper (square brackets for optional)
+ # $1 $2 $3 $4 $5
+ # [prefix][\]:directive:[spaces][param]newline
+ text.gsub!(/^([ \t]*#?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?\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?
prefix = $1
- directive = $2.downcase
- param = $4
+ directive = $3.downcase
+ param = $5
case directive
when 'include' then
filename = param.split[0]
- include_file filename, prefix
-
+ encoding = if defined?(Encoding) then text.encoding else nil end
+ include_file filename, prefix, encoding
else
result = yield directive, param if block_given?
@@ -88,27 +98,38 @@ class RDoc::Markup::PreProcess
end
##
- # Include a file, indenting it correctly.
-
- def include_file(name, indent)
- if full_name = find_include_file(name) then
- content = if defined?(Encoding) then
- File.binread full_name
- else
- File.read full_name
- end
- # HACK determine content type and force encoding
- 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
- else
+ # 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
+
+ # 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
diff --git a/lib/rdoc/markup/raw.rb b/lib/rdoc/markup/raw.rb
index 1124be7cc8..ca877c79af 100644
--- a/lib/rdoc/markup/raw.rb
+++ b/lib/rdoc/markup/raw.rb
@@ -27,6 +27,9 @@ class RDoc::Markup::Raw
self.class == other.class and text == other.text
end
+ ##
+ # Calls #accept_raw+ on +visitor+
+
def accept visitor
visitor.accept_raw self
end
@@ -63,3 +66,4 @@ class RDoc::Markup::Raw
end
end
+
diff --git a/lib/rdoc/markup/rule.rb b/lib/rdoc/markup/rule.rb
index 4fcd040d2b..b778f2bc09 100644
--- a/lib/rdoc/markup/rule.rb
+++ b/lib/rdoc/markup/rule.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Rule < Struct.new :weight
+ ##
+ # Calls #accept_rule on +visitor+
+
def accept visitor
visitor.accept_rule self
end
diff --git a/lib/rdoc/markup/text_formatter_test_case.rb b/lib/rdoc/markup/text_formatter_test_case.rb
new file mode 100644
index 0000000000..ba9e7c6187
--- /dev/null
+++ b/lib/rdoc/markup/text_formatter_test_case.rb
@@ -0,0 +1,116 @@
+require 'rdoc/markup/formatter_test_case'
+
+##
+# 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/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb
index 9a5be8babb..c9f874ea3c 100644
--- a/lib/rdoc/markup/to_ansi.rb
+++ b/lib/rdoc/markup/to_ansi.rb
@@ -1,10 +1,13 @@
-require 'rdoc/markup/inline'
+require 'rdoc/markup/to_rdoc'
##
# 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
super
@@ -23,12 +26,15 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
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
- @res << "\n"
+ @res << "\n" unless res.length == 1
2
else
bullet = @list_index.last.to_s
@@ -39,6 +45,9 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
@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
@@ -62,6 +71,9 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
end
end
+ ##
+ # Starts accepting with a reset screen
+
def start_accepting
super
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
index e7af129824..931edd81ea 100644
--- a/lib/rdoc/markup/to_bs.rb
+++ b/lib/rdoc/markup/to_bs.rb
@@ -1,4 +1,4 @@
-require 'rdoc/markup/inline'
+require 'rdoc/markup/to_rdoc'
##
# Outputs RDoc markup with hot backspace action! You will probably need a
@@ -8,6 +8,9 @@ require 'rdoc/markup/inline'
class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
+ ##
+ # Returns a new ToBs that is ready for hot backspace action!
+
def initialize
super
@@ -22,8 +25,12 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
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]
@@ -44,7 +51,6 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
when '+_' then @in_em = true
when '-_' then @in_em = false
end
-
''
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 74e3137eb2..de723921e9 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -8,6 +8,8 @@ require 'cgi'
class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
+ include RDoc::Text
+
##
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
@@ -15,7 +17,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
:BULLET => ['<ul>', '</ul>'],
:LABEL => ['<dl>', '</dl>'],
:LALPHA => ['<ol style="display: lower-alpha">', '</ol>'],
- :NOTE => ['<table>', '</table>'],
+ :NOTE => ['<table class="rdoc-list">', '</table>'],
:NUMBER => ['<ol>', '</ol>'],
:UALPHA => ['<ol style="display: upper-alpha">', '</ol>'],
}
@@ -48,6 +50,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
File.join(*from)
end
+ ##
+ # Creates a new formatter that will output HTML
+
def initialize
super
@@ -103,13 +108,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
end
+ # :section: Special handling
+
##
- # And we're invoked with a potential external hyperlink mailto:
- # just gets inserted. http: links are checked to see if they
+ # And we're invoked with a potential external hyperlink. <tt>mailto:</tt>
+ # just gets inserted. <tt>http:</tt> links are checked to see if they
# reference an image. If so, that image gets inserted using an
- # <img> tag. Otherwise a conventional <a href> is used. We also
- # support a special type of hyperlink, link:, which is a reference
- # to a local file whose path is relative to the --op directory.
+ # <tt><img></tt> tag. Otherwise a conventional <tt><a href></tt> is used.
+ # We also support a special type of hyperlink, <tt>link:</tt>, which is a
+ # reference to a local file whose path is relative to the --op directory.
def handle_special_HYPERLINK(special)
url = special.text
@@ -118,7 +125,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
##
# Here's a hypedlink where the label is different to the URL
- # <label>[url] or {long label}[url]
+ # <label>[url] or {long label}[url]
def handle_special_TIDYLINK(special)
text = special.text
@@ -130,8 +137,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
gen_url url, label
end
+ # :section: Utilities
+
##
- # This is a higher speed (if messier) version of wrap
+ # Wraps +txt+ to +line_len+
def wrap(txt, line_len = 76)
res = []
@@ -159,173 +168,150 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
sp += 1 while sp < ep and txt[sp] == ?\s
end
- res.join
+ res.join.strip
end
- ##
# :section: 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 +paragraph+ to the output
+
def accept_paragraph(paragraph)
- @res << annotate("<p>") + "\n"
- @res << wrap(convert_flow(@am.flow(paragraph.text)))
- @res << annotate("</p>") + "\n"
+ @res << "\n<p>"
+ @res << wrap(to_html(paragraph.text))
+ @res << "</p>\n"
end
+ ##
+ # Adds +verbatim+ to the output
+
def accept_verbatim(verbatim)
- @res << annotate("<pre>") << "\n"
- @res << CGI.escapeHTML(verbatim.text)
- @res << annotate("</pre>") << "\n"
+ @res << "\n<pre>"
+ @res << CGI.escapeHTML(verbatim.text.rstrip)
+ @res << "</pre>\n"
end
+ ##
+ # Adds +rule+ to the output
+
def accept_rule(rule)
size = rule.weight
size = 10 if size > 10
- @res << "<hr style=\"height: #{size}px\"></hr>"
+ @res << "<hr style=\"height: #{size}px\">\n"
end
+ ##
+ # Prepares the visitor for consuming +list+
+
def accept_list_start(list)
@list << list.type
- @res << html_list_name(list.type, true) << "\n"
+ @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 << annotate(tag) << "\n"
+ @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 << annotate(tag) << "\n"
+ @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
- def accept_blank_line(blank_line)
- # @res << annotate("<p />") << "\n"
- end
-
- def accept_heading(heading)
- @res << convert_heading(heading.level, @am.flow(heading.text))
- end
-
- def accept_raw raw
- @res << raw.parts.join("\n")
- end
-
- private
-
##
- # Converts string +item+
+ # Adds +blank_line+ to the output
- def convert_string(item)
- in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
+ def accept_blank_line(blank_line)
+ # @res << annotate("<p />") << "\n"
end
##
- # Escapes HTML in +item+
+ # Adds +heading+ to the output
- def convert_string_simple(item)
- CGI.escapeHTML item
+ def accept_heading(heading)
+ @res << "\n<h#{heading.level}>"
+ @res << to_html(heading.text)
+ @res << "</h#{heading.level}>\n"
end
##
- # Converts ampersand, dashes, elipsis, quotes, copyright and registered
- # trademark symbols to HTML escaped Unicode.
-
- def convert_string_fancy(item)
- # convert ampersand before doing anything else
- item.gsub(/&/, '&amp;').
-
- # convert -- to em-dash, (-- to en-dash)
- gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
-
- # convert ... to elipsis (and make sure .... becomes .<elipsis>)
- gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
+ # Adds +raw+ to the output
- # convert single closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;'). # }
- gsub(%r{\'(?=\W|s\b)}, '&#8217;').
-
- # convert single opening quote
- gsub(/'/, '&#8216;').
-
- # convert double closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1&#8221;'). # }
-
- # convert double opening quote
- gsub(/"/, '&#8220;').
-
- # convert copyright
- gsub(/\(c\)/, '&#169;').
-
- # convert registered trademark
- gsub(/\(r\)/, '&#174;')
+ def accept_raw raw
+ @res << raw.parts.join("\n")
end
##
- # Converts headings to hN elements
+ # CGI escapes +text+
- def convert_heading(level, flow)
- [annotate("<h#{level}>"),
- convert_flow(flow),
- annotate("</h#{level}>\n")].join
+ def convert_string(text)
+ CGI.escapeHTML text
end
##
- # Determins the HTML list element for +list_type+ and +open_tag+
+ # 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
- annotate tags[open_tag ? 0 : 1]
+ tags[open_tag ? 0 : 1]
end
##
- # Starts a list item
+ # 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
- annotate("<li>")
-
+ "<li>"
when :LABEL then
- annotate("<dt>") +
- convert_flow(@am.flow(list_item.label)) +
- annotate("</dt>") +
- annotate("<dd>")
-
+ "<dt>#{to_html list_item.label}</dt>\n<dd>"
when :NOTE then
- annotate("<tr>") +
- annotate("<td valign=\"top\">") +
- convert_flow(@am.flow(list_item.label)) +
- annotate("</td>") +
- annotate("<td>")
+ "<tr><td class=\"rdoc-term\"><p>#{to_html list_item.label}</p></td>\n<td>"
else
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
##
- # Ends a list item
+ # Returns the HTML end-tag for +list_type+
def list_end_for(list_type)
case list_type
@@ -340,5 +326,12 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
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/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
index 44e71486fb..a3feb848a2 100644
--- a/lib/rdoc/markup/to_html_crossref.rb
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -9,10 +9,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
##
# Regular expression to match class references
#
- # 1) There can be a '\' in front of text to suppress any cross-references
- # 2) There can be a '::' in front of class names to reference from the
+ # 1. There can be a '\\' in front of text to suppress the cross-reference
+ # 2. There can be a '::' in front of class names to reference from the
# top-level namespace.
- # 3) The method can be followed by parenthesis
+ # 3. The method can be followed by parenthesis (not recommended)
CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
@@ -34,10 +34,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# A::B::C.meth
#{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
- # Stand-alone method (proceeded by a #)
+ # Stand-alone method (preceeded by a #)
| \\?\##{METHOD_REGEXP_STR}
- # Stand-alone method (proceeded by ::)
+ # Stand-alone method (preceeded by ::)
| ::#{METHOD_REGEXP_STR}
# A::B::C
@@ -51,9 +51,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# In order that words like "can't" not
# be flagged as potential cross-references, only
# flag potential class cross-references if the character
- # after the cross-referece is a space or sentence
- # punctuation.
- | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;]|\z)
+ # after the cross-referece is a space, sentence
+ # punctuation, tag start character, or attribute
+ # marker.
+ | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z)
# Things that look like filenames
# The key thing is that there must be at least
@@ -62,7 +63,29 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
| (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+
# Things that have markup suppressed
- | \\[^\s]
+ # Don't process things like '\<' in \<tt>, though.
+ # TODO: including < is a hack, not very satisfying.
+ | \\[^\s<]
+ )/x
+
+ ##
+ # Version of CROSSREF_REGEXP used when <tt>--hyperlink-all</tt> is specified.
+
+ ALL_CROSSREF_REGEXP = /(
+ # A::B::C.meth
+ #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
+
+ # Stand-alone method
+ | \\?#{METHOD_REGEXP_STR}
+
+ # A::B::C
+ | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z)
+
+ # Things that look like filenames
+ | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+
+
+ # Things that have markup suppressed
+ | \\[^\s<]
)/x
##
@@ -71,19 +94,28 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
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.
+ # references are removed unless +show_hash+ is true. Only method names
+ # preceded by '#' or '::' are hyperlinked, unless +hyperlink_all+ is true.
- def initialize(from_path, context, show_hash)
+ def initialize(from_path, context, show_hash, hyperlink_all = false)
raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super()
- @markup.add_special(CROSSREF_REGEXP, :CROSSREF)
+ crossref_re = hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
+
+ @markup.add_special crossref_re, :CROSSREF
@from_path = from_path
@context = context
@show_hash = show_hash
+ @hyperlink_all = hyperlink_all
@seen = {}
end
@@ -92,22 +124,24 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# We're invoked when any text matches the CROSSREF pattern. If we find the
# corresponding reference, generate a hyperlink. If the name we're looking
# for contains no punctuation, we look for it up the module/class chain.
- # For example, HyperlinkHtml is found, even without the Generator:: prefix,
- # because we look for it in module Generator first.
+ # 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
- # 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/
+ 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
return @seen[name] if @seen.include? name
lookup = name
- name = name[0, 1] unless @show_hash if name[0, 1] == '#'
+ name = name[1..-1] unless @show_hash if name[0, 1] == '#'
# Find class, module, or method in class or module.
#
@@ -120,26 +154,47 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# whether the string as a whole is a known symbol).
if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then
- container = $1
type = $2
- type = '#' if type == '.'
+ type = '' if type == '.' # will find either #method or ::method
method = "#{type}#{$3}"
- ref = @context.find_symbol container, method
+ container = @context.find_symbol_module($1)
+ elsif /^([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then
+ type = $1
+ type = '' if type == '.'
+ method = "#{type}#{$2}"
+ container = @context
+ else
+ container = nil
+ end
+
+ if container then
+ ref = container.find_local_symbol method
+
+ unless ref || RDoc::TopLevel === container then
+ ref = container.find_ancestor_local_symbol method
+ end
end
ref = @context.find_symbol lookup unless ref
+ ref = nil if RDoc::Alias === ref # external alias: can't link to it
out = if lookup == '\\' then
lookup
elsif lookup =~ /^\\/ then
- $'
- elsif ref and ref.document_self then
- "<a href=\"#{ref.as_href @from_path}\">#{name}</a>"
+ # we remove the \ only in front of what we know:
+ # other backslashes are treated later, only outside of <tt>
+ ref ? $' : lookup
+ elsif ref then
+ if ref.document_self then
+ "<a href=\"#{ref.as_href @from_path}\">#{name}</a>"
+ else
+ name
+ end
else
- name
+ lookup
end
- @seen[name] = out
+ @seen[lookup] = out
out
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 867715bb1e..b1ac59e5b0 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
require 'rdoc/markup/inline'
##
@@ -5,21 +6,49 @@ require 'rdoc/markup/inline'
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
super
- @markup.add_special(/\\[^\s]/, :SUPPRESSED_CROSSREF)
-
+ @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
@width = 78
- @prefix = ''
-
init_tags
@headings = {}
@@ -34,7 +63,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
- # Maps attributes to ANSI sequences
+ # Maps attributes to HTML sequences
def init_tags
add_tag :BOLD, "<b>", "</b>"
@@ -42,10 +71,16 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
add_tag :EM, "<em>", "</em>"
end
+ ##
+ # Adds +blank_line+ to the output
+
def accept_blank_line blank_line
@res << "\n"
end
+ ##
+ # Adds +heading+ to the output
+
def accept_heading heading
use_prefix or @res << ' ' * @indent
@res << @headings[heading.level][0]
@@ -54,12 +89,18 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@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
@@ -76,29 +117,29 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@indent -= width
end
+ ##
+ # Prepares the visitor for consuming +list_item+
+
def accept_list_item_start list_item
- bullet = case @list_type.last
- when :BULLET then
- '*'
- when :NOTE, :LABEL then
- attributes(list_item.label) + ":\n"
- else
- @list_index.last.to_s + '.'
- end
-
- case @list_type.last
+ type = @list_type.last
+
+ case type
when :NOTE, :LABEL then
+ bullet = attributes(list_item.label) + ":\n"
+ @prefix = ' ' * @indent
@indent += 2
- @prefix = bullet + (' ' * @indent)
+ @prefix << bullet + (' ' * @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
@@ -123,14 +164,23 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@list_type << list.type
end
+ ##
+ # Adds +paragraph+ to the output
+
def accept_paragraph paragraph
wrap attributes(paragraph.text)
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)
@@ -138,58 +188,46 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
- # Outputs +verbatim+ flush left and indented 2 columns
+ # Outputs +verbatim+ indented 2 columns
def accept_verbatim verbatim
indent = ' ' * (@indent + 2)
- lines = []
- current_line = []
-
- # split into lines
verbatim.parts.each do |part|
- current_line << part
-
- if part == "\n" then
- lines << current_line
- current_line = []
- end
+ @res << indent unless part == "\n"
+ @res << part
end
- lines << current_line unless current_line.empty?
-
- # calculate margin
- indented = lines.select { |line| line != ["\n"] }
- margin = indented.map { |line| line.first.length }.min
-
- # flush left
- indented.each { |line| line[0][0...margin] = '' }
-
- # output
- use_prefix or @res << indent # verbatim is unlikely to have prefix
- @res << lines.shift.join
-
- lines.each do |line|
- @res << indent unless line == ["\n"]
- @res << line.join
- end
-
- @res << "\n"
+ @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 preceeding \\ from the suppressed crossref +special+
+
def handle_special_SUPPRESSED_CROSSREF special
- special.text.sub(/\\/, '')
+ text = special.text
+ text = text.sub('\\', '') unless in_tt?
+ text
end
+ ##
+ # Prepares the visitor for text generation
+
def start_accepting
@res = [""]
@indent = 0
@@ -200,6 +238,10 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@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
@@ -208,6 +250,9 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
prefix
end
+ ##
+ # Wraps +text+ to #width
+
def wrap text
return unless text && !text.empty?
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
index 0afdb96a18..f79f9475f1 100644
--- a/lib/rdoc/markup/to_test.rb
+++ b/lib/rdoc/markup/to_test.rb
@@ -6,6 +6,8 @@ require 'rdoc/markup/formatter'
class RDoc::Markup::ToTest < RDoc::Markup::Formatter
+ # :stopdoc:
+
##
# :section: Visitor
@@ -22,8 +24,12 @@ class RDoc::Markup::ToTest < RDoc::Markup::Formatter
@res << paragraph.text
end
+ def accept_raw raw
+ @res << raw.parts.join
+ end
+
def accept_verbatim(verbatim)
- @res << verbatim.text
+ @res << verbatim.text.gsub(/^(\S)/, ' \1')
end
def accept_list_start(list)
@@ -60,5 +66,7 @@ class RDoc::Markup::ToTest < RDoc::Markup::Formatter
@res << '-' * rule.weight
end
+ # :startdoc:
+
end
diff --git a/lib/rdoc/markup/verbatim.rb b/lib/rdoc/markup/verbatim.rb
index c684d78765..8fe2184699 100644
--- a/lib/rdoc/markup/verbatim.rb
+++ b/lib/rdoc/markup/verbatim.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Verbatim < RDoc::Markup::Raw
+ ##
+ # Calls #accept_verbatim on +visitor+
+
def accept visitor
visitor.accept_verbatim self
end
@@ -17,16 +20,16 @@ class RDoc::Markup::Verbatim < RDoc::Markup::Raw
@parts.each do |part|
case part
- when /\n/ then
+ when /^\s*\n/ then
newlines += 1
- parts << part if newlines <= 2
+ parts << part if newlines == 1
else
newlines = 0
parts << part
end
end
- parts.slice!(-1) if parts[-2..-1] == ["\n", "\n"]
+ parts.pop if parts.last =~ /\A\r?\n\z/
@parts = parts
end