Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/rdoc/markup/attr_span.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@ class RDoc::Markup::AttrSpan
##
# Creates a new AttrSpan for +length+ characters

def initialize(length)
def initialize(length, exclusive)
@attrs = Array.new(length, 0)
@exclusive = exclusive
end

##
# Toggles +bits+ from +start+ to +length+
def set_attrs(start, length, bits)
updated = false
for i in start ... (start+length)
@attrs[i] |= bits
if (@exclusive & @attrs[i]) == 0 || (@exclusive & bits) != 0
@attrs[i] |= bits
updated = true
end
end
updated
end

##
Expand Down
121 changes: 93 additions & 28 deletions lib/rdoc/markup/attribute_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class RDoc::Markup::AttributeManager

attr_reader :regexp_handlings

##
# A bits of exclusive maps
attr_reader :exclusive_bitmap

##
# Creates a new attribute manager that understands bold, emphasized and
# teletype text.
Expand All @@ -68,17 +72,18 @@ def initialize
@protectable = %w[<]
@regexp_handlings = []
@word_pair_map = {}
@exclusive_bitmap = 0
@attributes = RDoc::Markup::Attributes.new

add_word_pair "*", "*", :BOLD
add_word_pair "_", "_", :EM
add_word_pair "+", "+", :TT
add_word_pair "*", "*", :BOLD, true
add_word_pair "_", "_", :EM, true
add_word_pair "+", "+", :TT, true

add_html "em", :EM
add_html "i", :EM
add_html "b", :BOLD
add_html "tt", :TT
add_html "code", :TT
add_html "em", :EM, true
add_html "i", :EM, true
add_html "b", :BOLD, true
add_html "tt", :TT, true
add_html "code", :TT, true
end

##
Expand Down Expand Up @@ -122,29 +127,67 @@ def copy_string(start_pos, end_pos)
res
end

def exclusive?(attr)
(attr & @exclusive_bitmap) != 0
end

NON_PRINTING_START = "\1" # :nodoc:
NON_PRINTING_END = "\2" # :nodoc:

##
# 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)
def convert_attrs(str, attrs, exclusive = false)
convert_attrs_matching_word_pairs(str, attrs, exclusive)
convert_attrs_word_pair_map(str, attrs, exclusive)
end

def convert_attrs_matching_word_pairs(str, attrs, exclusive)
# first do matching ones
tags = @matching_word_pairs.keys.join("")
tags = @matching_word_pairs.select { |start, bitmap|
if exclusive && exclusive?(bitmap)
true
elsif !exclusive && !exclusive?(bitmap)
true
else
false
end
}.keys
return if tags.empty?
all_tags = @matching_word_pairs.keys

re = /(^|\W)([#{tags}])([#\\]?[\w:.\/-]+?\S?)\2(\W|$)/
re = /(^|\W|[#{all_tags.join("")}])([#{tags.join("")}])(\2*[#\\]?[\w:.\/\[\]-]+?\S?)\2(?!\2)([#{all_tags.join("")}]|\W|$)/

1 while str.gsub!(re) do
1 while str.gsub!(re) { |orig|
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
attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
if attr_updated
$1 + NULL * $2.length + $3 + NULL * $2.length + $4
else
$1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
end
}
str.delete!(NON_PRINTING_START + NON_PRINTING_END)
end

def convert_attrs_word_pair_map(str, attrs, exclusive)
# 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
if !exclusive
next if exclusive?(attr)
else
next if !exclusive?(attr)
end
1 while str.gsub!(regexp) { |orig|
updated = attrs.set_attrs($`.length + $1.length, $2.length, attr)
if updated
NULL * $1.length + $2 + NULL * $3.length
else
orig
end
}
end
end
Expand All @@ -153,10 +196,18 @@ def convert_attrs(str, attrs)
##
# Converts HTML tags to RDoc attributes

def convert_html(str, attrs)
tags = @html_tags.keys.join '|'
def convert_html(str, attrs, exclusive = false)
tags = @html_tags.select { |start, bitmap|
if exclusive && exclusive?(bitmap)
true
elsif !exclusive && !exclusive?(bitmap)
true
else
false
end
}.keys.join '|'

1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig|
attr = @html_tags[$1.downcase]
html_length = $1.length + 2
seq = NULL * html_length
Expand All @@ -168,8 +219,13 @@ def convert_html(str, attrs)
##
# Converts regexp handling sequences to RDoc attributes

def convert_regexp_handlings str, attrs
def convert_regexp_handlings str, attrs, exclusive = false
@regexp_handlings.each do |regexp, attribute|
if exclusive
next if !exclusive?(attribute)
else
next if exclusive?(attribute)
end
str.scan(regexp) do
capture = $~.size == 1 ? 0 : 1

Expand Down Expand Up @@ -205,7 +261,7 @@ def unmask_protected_sequences
#
# am.add_word_pair '*', '*', :BOLD

def add_word_pair(start, stop, name)
def add_word_pair(start, stop, name, exclusive = false)
raise ArgumentError, "Word flags may not start with '<'" if
start[0,1] == '<'

Expand All @@ -220,6 +276,8 @@ def add_word_pair(start, stop, name)

@protectable << start[0,1]
@protectable.uniq!

@exclusive_bitmap |= bitmap if exclusive
end

##
Expand All @@ -228,8 +286,10 @@ def add_word_pair(start, stop, name)
#
# am.add_html 'em', :EM

def add_html(tag, name)
@html_tags[tag.downcase] = @attributes.bitmap_for name
def add_html(tag, name, exclusive = false)
bitmap = @attributes.bitmap_for name
@html_tags[tag.downcase] = bitmap
@exclusive_bitmap |= bitmap if exclusive
end

##
Expand All @@ -238,8 +298,10 @@ def add_html(tag, name)
#
# @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)

def add_regexp_handling pattern, name
@regexp_handlings << [pattern, @attributes.bitmap_for(name)]
def add_regexp_handling pattern, name, exclusive = false
bitmap = @attributes.bitmap_for(name)
@regexp_handlings << [pattern, bitmap]
@exclusive_bitmap |= bitmap if exclusive
end

##
Expand All @@ -250,8 +312,11 @@ def flow str

mask_protected_sequences

@attrs = RDoc::Markup::AttrSpan.new @str.length
@attrs = RDoc::Markup::AttrSpan.new @str.length, @exclusive_bitmap

convert_attrs @str, @attrs, true
convert_html @str, @attrs, true
convert_regexp_handlings @str, @attrs, true
convert_attrs @str, @attrs
convert_html @str, @attrs
convert_regexp_handlings @str, @attrs
Expand Down
25 changes: 22 additions & 3 deletions test/rdoc/test_rdoc_markup_attribute_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,25 @@ def test_combined

def test_convert_attrs
str = '+foo+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap

@am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs

assert_equal "\000foo\000", str

str = '+:foo:+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap

@am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs

assert_equal "\000:foo:\000", str

str = '+x-y+'.dup
attrs = RDoc::Markup::AttrSpan.new str.length
attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap

@am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs

assert_equal "\000x-y\000", str
Expand Down Expand Up @@ -243,6 +246,22 @@ def test_escapes
output('unhandled <p>tag</p> unchanged')
end

def test_exclude_tag
assert_equal '<CODE>aaa</CODE>[:symbol]', output('+aaa+[:symbol]')
assert_equal '<CODE>aaa[:symbol]</CODE>', output('+aaa[:symbol]+')
assert_equal 'aaa[:symbol]', output('aaa[:symbol]')
assert_equal '<B><CODE>index</CODE></B>', output('<b><tt>index</tt></b>')
end

def test_exclude_tag_flow
assert_equal [@tt_on, "aaa", @tt_off, "[:symbol]"],
@am.flow("+aaa+[:symbol]")
assert_equal [@tt_on, "aaa[:symbol]", @tt_off],
@am.flow("+aaa[:symbol]+")
assert_equal ["aaa[:symbol]"],
@am.flow("aaa[:symbol]")
end

def test_html_like_em_bold
assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
@am.flow("cat <i>and </i><b>dog</b>")
Expand Down
11 changes: 11 additions & 0 deletions test/rdoc/test_rdoc_markup_to_html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,17 @@ def test_convert_TIDYLINK_irc
assert_equal "\n<p><a href=\"irc://irc.freenode.net/#ruby-lang\">ruby-lang</a></p>\n", result
end

def test_convert_with_exclude_tag
assert_equal "\n<p><code>aaa</code>[:symbol]</p>\n", @to.convert('+aaa+[:symbol]')
assert_equal "\n<p><code>aaa[:symbol]</code></p>\n", @to.convert('+aaa[:symbol]+')
assert_equal "\n<p><a href=\":symbol\">aaa</a></p>\n", @to.convert('aaa[:symbol]')
end

def test_convert_underscore_adjacent_to_code
assert_equal "\n<p><code>aaa</code>_</p>\n", @to.convert(%q{+aaa+_})
assert_equal "\n<p>`<code>i386-mswin32_</code><em>MSRTVERSION</em>&#39;</p>\n", @to.convert(%q{`+i386-mswin32_+_MSRTVERSION_'})
end

def test_gen_url
assert_equal '<a href="example">example</a>',
@to.gen_url('link:example', 'example')
Expand Down