@@ -95,6 +95,12 @@ def initialize
9595 add_html "b" , :BOLD , true
9696 add_html "tt" , :TT , true
9797 add_html "code" , :TT , true
98+
99+ @word_pair_chars = @matching_word_pairs . keys . join
100+
101+ # Matches a word pair delimiter (*, _, +) that is NOT already protected.
102+ # Used by #protect_code_markup to escape delimiters inside <code>/<tt> tags.
103+ @unprotected_word_pair_regexp = /([#{ @word_pair_chars } ])(?!#{ PROTECT_ATTR } )/
98104 end
99105
100106 ##
@@ -164,7 +170,7 @@ def convert_attrs_matching_word_pairs(str, attrs, exclusive)
164170 } . keys
165171 return if tags . empty?
166172 tags = "[#{ tags . join ( "" ) } ](?!#{ PROTECT_ATTR } )"
167- all_tags = "[#{ @matching_word_pairs . keys . join ( "" ) } ](?!#{ PROTECT_ATTR } )"
173+ all_tags = "[#{ @word_pair_chars } ](?!#{ PROTECT_ATTR } )"
168174
169175 re = /(?:^|\W |#{ all_tags } )\K (#{ tags } )(\1 *[#\\ ]?[\w :#{ PROTECT_ATTR } .\/ \[ \] -]+?\S ?)\1 (?!\1 )(?=#{ all_tags } |\W |$)/
170176
@@ -245,6 +251,20 @@ def mask_protected_sequences
245251 @str . gsub! ( /\\ (\\ [#{ Regexp . escape @protectable . join } ])/m , "\\ 1" )
246252 end
247253
254+ ##
255+ # Protects word pair delimiters (*, _, +) inside
256+ # <code> and <tt> tags from being processed as inline formatting.
257+ # For example, *bold* in +*bold*+ will NOT be rendered as bold.
258+
259+ def protect_code_markup
260+ @str . gsub! ( /<(code|tt)>(.*?)<\/ \1 >/im ) do
261+ tag = $1
262+ content = $2
263+ escaped = content . gsub ( @unprotected_word_pair_regexp , "\\ 1#{ PROTECT_ATTR } " )
264+ "<#{ tag } >#{ escaped } </#{ tag } >"
265+ end
266+ end
267+
248268 ##
249269 # Unescapes regexp handling sequences of text
250270
@@ -308,6 +328,7 @@ def flow(str)
308328 @str = str . dup
309329
310330 mask_protected_sequences
331+ protect_code_markup
311332
312333 @attrs = RDoc ::Markup ::AttrSpan . new @str . length , @exclusive_bitmap
313334
0 commit comments