@@ -58,6 +58,10 @@ class RDoc::Markup::AttributeManager
58
58
59
59
attr_reader :regexp_handlings
60
60
61
+ ##
62
+ # A bits of exclusive maps
63
+ attr_reader :exclusive_bitmap
64
+
61
65
##
62
66
# Creates a new attribute manager that understands bold, emphasized and
63
67
# teletype text.
@@ -68,17 +72,18 @@ def initialize
68
72
@protectable = %w[ < ]
69
73
@regexp_handlings = [ ]
70
74
@word_pair_map = { }
75
+ @exclusive_bitmap = 0
71
76
@attributes = RDoc ::Markup ::Attributes . new
72
77
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
76
81
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
82
87
end
83
88
84
89
##
@@ -122,29 +127,67 @@ def copy_string(start_pos, end_pos)
122
127
res
123
128
end
124
129
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
+
125
137
##
126
138
# Map attributes like <b>text</b>to the sequence
127
139
# \001\002<char>\001\003<char>, where <char> is a per-attribute specific
128
140
# character
129
141
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 )
131
148
# 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
133
160
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 |$)/
135
162
136
- 1 while str . gsub! ( re ) do
163
+ 1 while str . gsub! ( re ) { | orig |
137
164
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
141
174
175
+ def convert_attrs_word_pair_map ( str , attrs , exclusive )
142
176
# then non-matching
143
177
unless @word_pair_map . empty? then
144
178
@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
148
191
}
149
192
end
150
193
end
@@ -153,10 +196,18 @@ def convert_attrs(str, attrs)
153
196
##
154
197
# Converts HTML tags to RDoc attributes
155
198
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 '|'
158
209
159
- 1 while str . gsub! ( /<(#{ tags } )>(.*?)<\/ \1 >/i ) {
210
+ 1 while str . gsub! ( /<(#{ tags } )>(.*?)<\/ \1 >/i ) { | orig |
160
211
attr = @html_tags [ $1. downcase ]
161
212
html_length = $1. length + 2
162
213
seq = NULL * html_length
@@ -168,8 +219,13 @@ def convert_html(str, attrs)
168
219
##
169
220
# Converts regexp handling sequences to RDoc attributes
170
221
171
- def convert_regexp_handlings str , attrs
222
+ def convert_regexp_handlings str , attrs , exclusive = false
172
223
@regexp_handlings . each do |regexp , attribute |
224
+ if exclusive
225
+ next if !exclusive? ( attribute )
226
+ else
227
+ next if exclusive? ( attribute )
228
+ end
173
229
str . scan ( regexp ) do
174
230
capture = $~. size == 1 ? 0 : 1
175
231
@@ -205,7 +261,7 @@ def unmask_protected_sequences
205
261
#
206
262
# am.add_word_pair '*', '*', :BOLD
207
263
208
- def add_word_pair ( start , stop , name )
264
+ def add_word_pair ( start , stop , name , exclusive = false )
209
265
raise ArgumentError , "Word flags may not start with '<'" if
210
266
start [ 0 , 1 ] == '<'
211
267
@@ -220,6 +276,8 @@ def add_word_pair(start, stop, name)
220
276
221
277
@protectable << start [ 0 , 1 ]
222
278
@protectable . uniq!
279
+
280
+ @exclusive_bitmap |= bitmap if exclusive
223
281
end
224
282
225
283
##
@@ -228,8 +286,10 @@ def add_word_pair(start, stop, name)
228
286
#
229
287
# am.add_html 'em', :EM
230
288
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
233
293
end
234
294
235
295
##
@@ -238,8 +298,10 @@ def add_html(tag, name)
238
298
#
239
299
# @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)
240
300
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
243
305
end
244
306
245
307
##
@@ -250,8 +312,11 @@ def flow str
250
312
251
313
mask_protected_sequences
252
314
253
- @attrs = RDoc ::Markup ::AttrSpan . new @str . length
315
+ @attrs = RDoc ::Markup ::AttrSpan . new @str . length , @exclusive_bitmap
254
316
317
+ convert_attrs @str , @attrs , true
318
+ convert_html @str , @attrs , true
319
+ convert_regexp_handlings @str , @attrs , true
255
320
convert_attrs @str , @attrs
256
321
convert_html @str , @attrs
257
322
convert_regexp_handlings @str , @attrs
0 commit comments