summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2023-05-17 17:02:28 -0600
committerGitHub <noreply@github.com>2023-05-17 17:02:28 -0600
commit722157753051e36e2a5dca3c4d95de51a2fae157 (patch)
tree9b0b8040e31f034167a92ca158ee5a26c4794553
parent6b2ab72ffccd653ad2daf3e05c3712d3f1ad4708 (diff)
resolves #4458 process constrained inline passthrough inside monospace span (PR #4459)
-rw-r--r--CHANGELOG.adoc1
-rw-r--r--lib/asciidoctor/rx.rb18
-rw-r--r--lib/asciidoctor/substitutors.rb39
-rw-r--r--test/substitutions_test.rb36
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