@@ -58,6 +58,10 @@ class RDoc::Markup::AttributeManager
5858
5959 attr_reader :regexp_handlings
6060
61+ ##
62+ # A bits of exclusive maps
63+ attr_reader :exclusive_bitmap
64+
6165 ##
6266 # Creates a new attribute manager that understands bold, emphasized and
6367 # teletype text.
@@ -68,17 +72,18 @@ def initialize
6872 @protectable = %w[ < ]
6973 @regexp_handlings = [ ]
7074 @word_pair_map = { }
75+ @exclusive_bitmap = 0
7176 @attributes = RDoc ::Markup ::Attributes . new
7277
73- add_word_pair "*" , "*" , :BOLD
74- add_word_pair "_" , "_" , :EM
75- add_word_pair "+" , "+" , :TT
78+ add_word_pair "*" , "*" , :BOLD , true
79+ add_word_pair "_" , "_" , :EM , true
80+ add_word_pair "+" , "+" , :TT , true
7681
77- add_html "em" , :EM
78- add_html "i" , :EM
79- add_html "b" , :BOLD
80- add_html "tt" , :TT
81- add_html "code" , :TT
82+ add_html "em" , :EM , true
83+ add_html "i" , :EM , true
84+ add_html "b" , :BOLD , true
85+ add_html "tt" , :TT , true
86+ add_html "code" , :TT , true
8287 end
8388
8489 ##
@@ -122,29 +127,67 @@ def copy_string(start_pos, end_pos)
122127 res
123128 end
124129
130+ def exclusive? ( attr )
131+ ( attr & @exclusive_bitmap ) != 0
132+ end
133+
134+ NON_PRINTING_START = "\1 " # :nodoc:
135+ NON_PRINTING_END = "\2 " # :nodoc:
136+
125137 ##
126138 # Map attributes like <b>text</b>to the sequence
127139 # \001\002<char>\001\003<char>, where <char> is a per-attribute specific
128140 # character
129141
130- def convert_attrs ( str , attrs )
142+ def convert_attrs ( str , attrs , exclusive = false )
143+ convert_attrs_matching_word_pairs ( str , attrs , exclusive )
144+ convert_attrs_word_pair_map ( str , attrs , exclusive )
145+ end
146+
147+ def convert_attrs_matching_word_pairs ( str , attrs , exclusive )
131148 # first do matching ones
132- tags = @matching_word_pairs . keys . join ( "" )
149+ tags = @matching_word_pairs . select { |start , bitmap |
150+ if exclusive && exclusive? ( bitmap )
151+ true
152+ elsif !exclusive && !exclusive? ( bitmap )
153+ true
154+ else
155+ false
156+ end
157+ } . keys
158+ return if tags . empty?
159+ all_tags = @matching_word_pairs . keys
133160
134- re = /(^|\W ) ([#{ tags } ])([#\\ ]?[\w :.\/ -]+?\S ?)\2 (\W |$)/
161+ re = /(^|\W |[ #{ all_tags . join ( "" ) } ]) ([#{ tags . join ( "" ) } ])(\2 * [#\\ ]?[\w :.\/ \[ \] -]+?\S ?)\2 (?! \2 )([ #{ all_tags . join ( "" ) } ]| \W |$)/
135162
136- 1 while str . gsub! ( re ) do
163+ 1 while str . gsub! ( re ) { | orig |
137164 attr = @matching_word_pairs [ $2]
138- attrs . set_attrs ( $`. length + $1. length + $2. length , $3. length , attr )
139- $1 + NULL * $2. length + $3 + NULL * $2. length + $4
140- end
165+ attr_updated = attrs . set_attrs ( $`. length + $1. length + $2. length , $3. length , attr )
166+ if attr_updated
167+ $1 + NULL * $2. length + $3 + NULL * $2. length + $4
168+ else
169+ $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
170+ end
171+ }
172+ str . delete! ( NON_PRINTING_START + NON_PRINTING_END )
173+ end
141174
175+ def convert_attrs_word_pair_map ( str , attrs , exclusive )
142176 # then non-matching
143177 unless @word_pair_map . empty? then
144178 @word_pair_map . each do |regexp , attr |
145- str . gsub! ( regexp ) {
146- attrs . set_attrs ( $`. length + $1. length , $2. length , attr )
147- NULL * $1. length + $2 + NULL * $3. length
179+ if !exclusive
180+ next if exclusive? ( attr )
181+ else
182+ next if !exclusive? ( attr )
183+ end
184+ 1 while str . gsub! ( regexp ) { |orig |
185+ updated = attrs . set_attrs ( $`. length + $1. length , $2. length , attr )
186+ if updated
187+ NULL * $1. length + $2 + NULL * $3. length
188+ else
189+ orig
190+ end
148191 }
149192 end
150193 end
@@ -153,10 +196,18 @@ def convert_attrs(str, attrs)
153196 ##
154197 # Converts HTML tags to RDoc attributes
155198
156- def convert_html ( str , attrs )
157- tags = @html_tags . keys . join '|'
199+ def convert_html ( str , attrs , exclusive = false )
200+ tags = @html_tags . select { |start , bitmap |
201+ if exclusive && exclusive? ( bitmap )
202+ true
203+ elsif !exclusive && !exclusive? ( bitmap )
204+ true
205+ else
206+ false
207+ end
208+ } . keys . join '|'
158209
159- 1 while str . gsub! ( /<(#{ tags } )>(.*?)<\/ \1 >/i ) {
210+ 1 while str . gsub! ( /<(#{ tags } )>(.*?)<\/ \1 >/i ) { | orig |
160211 attr = @html_tags [ $1. downcase ]
161212 html_length = $1. length + 2
162213 seq = NULL * html_length
@@ -168,8 +219,13 @@ def convert_html(str, attrs)
168219 ##
169220 # Converts regexp handling sequences to RDoc attributes
170221
171- def convert_regexp_handlings str , attrs
222+ def convert_regexp_handlings str , attrs , exclusive = false
172223 @regexp_handlings . each do |regexp , attribute |
224+ if exclusive
225+ next if !exclusive? ( attribute )
226+ else
227+ next if exclusive? ( attribute )
228+ end
173229 str . scan ( regexp ) do
174230 capture = $~. size == 1 ? 0 : 1
175231
@@ -205,7 +261,7 @@ def unmask_protected_sequences
205261 #
206262 # am.add_word_pair '*', '*', :BOLD
207263
208- def add_word_pair ( start , stop , name )
264+ def add_word_pair ( start , stop , name , exclusive = false )
209265 raise ArgumentError , "Word flags may not start with '<'" if
210266 start [ 0 , 1 ] == '<'
211267
@@ -220,6 +276,8 @@ def add_word_pair(start, stop, name)
220276
221277 @protectable << start [ 0 , 1 ]
222278 @protectable . uniq!
279+
280+ @exclusive_bitmap |= bitmap if exclusive
223281 end
224282
225283 ##
@@ -228,8 +286,10 @@ def add_word_pair(start, stop, name)
228286 #
229287 # am.add_html 'em', :EM
230288
231- def add_html ( tag , name )
232- @html_tags [ tag . downcase ] = @attributes . bitmap_for name
289+ def add_html ( tag , name , exclusive = false )
290+ bitmap = @attributes . bitmap_for name
291+ @html_tags [ tag . downcase ] = bitmap
292+ @exclusive_bitmap |= bitmap if exclusive
233293 end
234294
235295 ##
@@ -238,8 +298,10 @@ def add_html(tag, name)
238298 #
239299 # @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)
240300
241- def add_regexp_handling pattern , name
242- @regexp_handlings << [ pattern , @attributes . bitmap_for ( name ) ]
301+ def add_regexp_handling pattern , name , exclusive = false
302+ bitmap = @attributes . bitmap_for ( name )
303+ @regexp_handlings << [ pattern , bitmap ]
304+ @exclusive_bitmap |= bitmap if exclusive
243305 end
244306
245307 ##
@@ -250,8 +312,11 @@ def flow str
250312
251313 mask_protected_sequences
252314
253- @attrs = RDoc ::Markup ::AttrSpan . new @str . length
315+ @attrs = RDoc ::Markup ::AttrSpan . new @str . length , @exclusive_bitmap
254316
317+ convert_attrs @str , @attrs , true
318+ convert_html @str , @attrs , true
319+ convert_regexp_handlings @str , @attrs , true
255320 convert_attrs @str , @attrs
256321 convert_html @str , @attrs
257322 convert_regexp_handlings @str , @attrs
0 commit comments