diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2023-05-17 17:02:28 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-17 17:02:28 -0600 |
| commit | 722157753051e36e2a5dca3c4d95de51a2fae157 (patch) | |
| tree | 9b0b8040e31f034167a92ca158ee5a26c4794553 | |
| parent | 6b2ab72ffccd653ad2daf3e05c3712d3f1ad4708 (diff) | |
resolves #4458 process constrained inline passthrough inside monospace span (PR #4459)
| -rw-r--r-- | CHANGELOG.adoc | 1 | ||||
| -rw-r--r-- | lib/asciidoctor/rx.rb | 18 | ||||
| -rw-r--r-- | lib/asciidoctor/substitutors.rb | 39 | ||||
| -rw-r--r-- | test/substitutions_test.rb | 36 |
4 files changed, 55 insertions, 39 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d436a1c0..7a188ef1 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -73,6 +73,7 @@ Improvements:: Bug Fixes:: + * Process constrained inline passthrough inside monospace span (#4458) * Catalog inline ref defined using anchor macro even when resolved reftext is empty * Use while loop rather than recursion to locate next line to process; prevents stack limit error (#4368) * Avoid numeric character reference when looking for fragment in target of xref (#4393, #3642) diff --git a/lib/asciidoctor/rx.rb b/lib/asciidoctor/rx.rb index 39fb0fd3..2fabe0dc 100644 --- a/lib/asciidoctor/rx.rb +++ b/lib/asciidoctor/rx.rb @@ -569,23 +569,17 @@ module Asciidoctor # Examples # # +text+ - # `text` (compat) + # [x-]+text+ + # [x-]`text` + # `text` (compat only) + # [role]`text` (compat only) # # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior InlinePassRx = { - false => ['+', '`', /(^|[^#{CC_WORD};:])(?:#{QuoteAttributeListRxt})?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m], - true => ['`', nil, /(^|[^`#{CC_WORD}])(?:#{QuoteAttributeListRxt})?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m], + false => ['+', '-]', /((?:^|[^#{CC_WORD};:\\])(?=(\[)|\+)|\\(?=\[)|(?=\\\+))(?:\2(x-|[^\[\]]+ x-)\]|(?:#{QuoteAttributeListRxt})?(?=(\\)?\+))(\5?(\+|`)(\S|\S#{CC_ALL}*?\S)\7)(?!#{CG_WORD})/m], + true => ['`', nil, /(^|[^`#{CC_WORD}])(?:(\Z)()|#{QuoteAttributeListRxt}(?=(\\))?)?(\5?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\7)(?![`#{CC_WORD}])/m], } - # Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly - # inside constrained monospaced formatting in non-compat mode. - # - # Examples - # - # +text+ - # - SinglePlusInlinePassRx = /^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m - # Matches several variants of the passthrough inline macro, which may span multiple lines. # # Examples diff --git a/lib/asciidoctor/substitutors.rb b/lib/asciidoctor/substitutors.rb index af850790..d88e0d74 100644 --- a/lib/asciidoctor/substitutors.rb +++ b/lib/asciidoctor/substitutors.rb @@ -1065,10 +1065,11 @@ module Substitutors pass_inline_char1, pass_inline_char2, pass_inline_rx = InlinePassRx[compat_mode] text = text.gsub pass_inline_rx do preceding = $1 - attrlist = $2 - escape_mark = RS if (quoted_text = $3).start_with? RS - backtick_mark = $4 == '`' - content = $5 + attrlist = $4 || $3 + escaped = true if $5 + quoted_text = $6 + format_mark = $7 + content = $8 if compat_mode old_behavior = true @@ -1077,32 +1078,30 @@ module Substitutors end if attrlist - if backtick_mark && !old_behavior - next extract_inner_passthrough content, %(#{preceding}[#{attrlist}]#{escape_mark}) - elsif escape_mark + if escaped # honor the escape of the formatting mark - next %(#{preceding}[#{attrlist}]#{old_behavior_forced && backtick_mark ? quoted_text : (quoted_text.slice 1, quoted_text.length)}) + next %(#{preceding}[#{attrlist}]#{quoted_text.slice 1, quoted_text.length}) elsif preceding == RS # honor the escape of the attributes - next %(#{preceding}[#{attrlist}]#{quoted_text}) if old_behavior_forced && backtick_mark + next %(#{preceding}[#{attrlist}]#{quoted_text}) if old_behavior_forced && format_mark == '`' preceding = %([#{attrlist}]) elsif old_behavior_forced attributes = attrlist == 'x-' ? {} : (parse_quoted_text_attributes attrlist.slice 0, attrlist.length - 3) else attributes = parse_quoted_text_attributes attrlist end - elsif backtick_mark && !old_behavior - next extract_inner_passthrough content, %(#{preceding}#{escape_mark}) - elsif escape_mark + elsif escaped # honor the escape of the formatting mark next %(#{preceding}#{quoted_text.slice 1, quoted_text.length}) + elsif compat_mode && preceding == RS + next quoted_text end if compat_mode passthrus[passthru_key = passthrus.size] = { text: content, subs: BASIC_SUBS, attributes: attributes, type: :monospaced } elsif attributes if old_behavior - subs = backtick_mark ? BASIC_SUBS : NORMAL_SUBS + subs = format_mark == '`' ? BASIC_SUBS : NORMAL_SUBS passthrus[passthru_key = passthrus.size] = { text: content, subs: subs, attributes: attributes, type: :monospaced } else passthrus[passthru_key = passthrus.size] = { text: content, subs: BASIC_SUBS, attributes: attributes, type: :unquoted } @@ -1400,20 +1399,6 @@ module Substitutors end.join LF) end - # Internal: Extract nested single-plus passthrough; otherwise return unprocessed - def extract_inner_passthrough text, pre - if (text.end_with? '+') && (text.start_with? '+', '\+') && SinglePlusInlinePassRx =~ text - if $1 - %(#{pre}`+#{$2}+`) - else - @passthroughs[passthru_key = @passthroughs.size] = { text: $2, subs: BASIC_SUBS } - %(#{pre}`#{PASS_START}#{passthru_key}#{PASS_END}`) - end - else - %(#{pre}`#{text}`) - end - end - # Internal: Convert a quoted text region # # match - The MatchData for the quoted text region diff --git a/test/substitutions_test.rb b/test/substitutions_test.rb index 7b57c6d8..cbb69cbc 100644 --- a/test/substitutions_test.rb +++ b/test/substitutions_test.rb @@ -2154,6 +2154,42 @@ context 'Substitutions' do assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content end + test 'should support constrained passthrough in middle of monospace span' do + input = 'a `foo +bar+ baz` kind of thing' + para = block_from_string input + assert_equal 'a <code>foo bar baz</code> kind of thing', para.content + end + + test 'should support constrained passthrough in monospace span preceded by escaped boxed attrlist with transitional role' do + input = %(#{BACKSLASH}[x-]`foo +bar+ baz`) + para = block_from_string input + assert_equal '[x-]<code>foo bar baz</code>', para.content + end + + test 'should treat monospace phrase with escaped boxed attrlist with transitional role as monospace' do + input = %(#{BACKSLASH}[x-]`*foo* +bar+ baz`) + para = block_from_string input + assert_equal '[x-]<code><strong>foo</strong> bar baz</code>', para.content + end + + test 'should ignore escaped attrlist with transitional role on monospace phrase if not proceeded by [' do + input = %(#{BACKSLASH}x-]`*foo* +bar+ baz`) + para = block_from_string input + assert_equal %(#{BACKSLASH}x-]<code><strong>foo</strong> bar baz</code>), para.content + end + + test 'should not process passthrough inside transitional literal monospace span' do + input = 'a [x-]`foo +bar+ baz` kind of thing' + para = block_from_string input + assert_equal 'a <code>foo +bar+ baz</code> kind of thing', para.content + end + + test 'should support constrained passthrough in monospace phrase with attrlist' do + input = '[.role]`foo +bar+ baz`' + para = block_from_string input + assert_equal '<code class="role">foo bar baz</code>', para.content + end + test 'should support attrlist on a literal monospace phrase' do input = '[.baz]`+foo--bar+`' para = block_from_string input |
