Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable other notations in <code> tags #799

Merged
merged 3 commits into from
Mar 16, 2021
Merged
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
@@ -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

##
121 changes: 93 additions & 28 deletions lib/rdoc/markup/attribute_manager.rb
Original file line number Diff line number Diff line change
@@ -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.
@@ -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

##
@@ -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
@@ -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
@@ -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

@@ -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] == '<'

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

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

@exclusive_bitmap |= bitmap if exclusive
end

##
@@ -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

##
@@ -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

##
@@ -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
25 changes: 22 additions & 3 deletions test/rdoc/test_rdoc_markup_attribute_manager.rb
Original file line number Diff line number Diff line change
@@ -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
@@ -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>")
11 changes: 11 additions & 0 deletions test/rdoc/test_rdoc_markup_to_html.rb
Original file line number Diff line number Diff line change
@@ -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')