summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2014-07-27 17:33:39 -0600
committerDan Allen <dan.j.allen@gmail.com>2014-07-27 17:33:39 -0600
commit7cf68acb9a95ffdc54ec0b6374a009f20e84806b (patch)
tree58482bb4e9a55e065d48005c78eae899b8f43da4
parent2dd757f561611383daf6a1006c68b2885448427d (diff)
parent7d5e459f001d32d38b62d1ea1a2c196705b6dc11 (diff)
Merge pull request #1006 from mojavelinux/issue-718
resolves #718 and #714 swap + and ` formatting chars
-rw-r--r--compat/asciidoc.conf20
-rw-r--r--features/text_formatting.feature30
-rw-r--r--lib/asciidoctor.rb49
-rw-r--r--lib/asciidoctor/abstract_node.rb2
-rw-r--r--lib/asciidoctor/document.rb1
-rw-r--r--lib/asciidoctor/substitutors.rb168
-rw-r--r--test/attributes_test.rb9
-rw-r--r--test/extensions_test.rb2
-rw-r--r--test/substitutions_test.rb131
-rw-r--r--test/text_test.rb43
10 files changed, 348 insertions, 107 deletions
diff --git a/compat/asciidoc.conf b/compat/asciidoc.conf
index 94784bb4..d7a96d10 100644
--- a/compat/asciidoc.conf
+++ b/compat/asciidoc.conf
@@ -21,8 +21,12 @@ space=" "
tilde=~
user-home={eval:os.path.expanduser('~')}
vbar=|
-ifndef::compat-mode[]
+# NOTE use -a no-inline-literal to set compat-mode to default when using AsciiDoc Python
+ifdef::no-inline-literal[]
compat-mode=default
+endif::no-inline-literal[]
+ifndef::no-inline-literal[]
+compat-mode=legacy
endif::[]
[replacements]
@@ -36,6 +40,13 @@ endif::[]
# this change can also be made in the document using the attribute entry :quotes.':
ifeval::["{compat-mode}"!="legacy"]
'=
++=
+++=
+`=
+``=#monospaced
+`|`=monospaced
+\##=#mark
+\#=mark
endif::[]
# enables markdown-style headings
@@ -118,6 +129,11 @@ delimiter=^:={3,}$
format=dsv
[macros]
+ifdef::no-inline-literal[]
+(?su)\\?\+\+(?P<passtext>.*?)\+\+=pass[specialcharacters]
+(?su)(?<![+\w])(\\?\+(?P<passtext>\S|\S.*?\S)\+)(?![+\w])=pass[specialcharacters]
+endif::no-inline-literal[]
+
# additional callout match behind line comments
#(?://|#|;;) ?\((?P<index>\d+)\)=callout
# additional callout match for XML
@@ -170,7 +186,7 @@ strong=<strong{1? class="{1}"}>|</strong>
monospaced=<code{1? class="{1}"}>|</code>
superscript=<sup{1? class="{1}"}>|</sup>
subscript=<sub{1? class="{1}"}>|</sub>
-unquoted={1=<mark>}{1?<span class="{1}">}|{1?</span>}{1=</mark>}
+mark={1=<mark>}{1?<span class="{1}">}|{1?</span>}{1=</mark>}
[monospacedwords]
<code>{words}</code>
diff --git a/features/text_formatting.feature b/features/text_formatting.feature
index 625fe697..4517c6e8 100644
--- a/features/text_formatting.feature
+++ b/features/text_formatting.feature
@@ -5,7 +5,7 @@ Feature: Text Formatting
I want to be able to markup inline text with formatting characters
- Scenario: Render text that contains superscript and subscript characters
+ Scenario: Convert text that contains superscript and subscript characters
Given the AsciiDoc source
"""
_v_~rocket~ is the value
@@ -25,3 +25,31 @@ Feature: Text Formatting
the 10<sup>th</sup> point has coordinate (x<sub>10</sub>, y<sub>10</sub>)</p>
</div>
"""
+
+
+ Scenario: Convert text that has ex-inline literal formatting
+ Given the AsciiDoc source
+ """
+ Use [x-]`{asciidoctor-version}` to print the version of Asciidoctor.
+ """
+ When it is converted to html
+ Then the result should match the HTML source
+ """
+ <div class="paragraph">
+ <p>Use <code>{asciidoctor-version}</code> to print the version of Asciidoctor.</p>
+ </div>
+ """
+
+
+ Scenario: Convert text that has ex-inline monospaced formatting
+ Given the AsciiDoc source
+ """
+ The document is assumed to be encoded as [x-]+{encoding}+.
+ """
+ When it is converted to html
+ Then the result should match the HTML source
+ """
+ <div class="paragraph">
+ <p>The document is assumed to be encoded as <code>UTF-8</code>.</p>
+ </div>
+ """
diff --git a/lib/asciidoctor.rb b/lib/asciidoctor.rb
index d099b395..41648361 100644
--- a/lib/asciidoctor.rb
+++ b/lib/asciidoctor.rb
@@ -906,13 +906,18 @@ module Asciidoctor
#
MenuInlineRx = /\\?"(#{CG_WORD}[^"]*?#{CG_BLANK}*&gt;#{CG_BLANK}*[^" \t][^"]*)"/
- # Matches a passthrough literal value, which may span multiple lines.
+ # Matches an inline passthrough value, which may span multiple lines.
#
# Examples
#
- # `text`
+ # +text+
+ # `text` (legacy)
#
- PassInlineLiteralRx = /(^|[^`#{CC_WORD}])(?:\[([^\]]+?)\])?(\\?`([^`\s]|[^`\s].*?\S)`)(?![`#{CC_WORD}])/m
+ # NOTE we always capture the attributes so we know when to use legacy behavior
+ PassInlineRx = {
+ :default => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+?)\])?(\\?(\+|`)(\S|\S.*?\S)\4)(?!#{CC_WORD})/m],
+ :legacy => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+?)\])?(\\?(`)([^`\s]|[^`\s].*?\S)\4)(?![`#{CC_WORD}])/m]
+ }
# Matches several variants of the passthrough inline macro, which may span multiple lines.
#
@@ -922,7 +927,7 @@ module Asciidoctor
# $$text$$
# pass:quotes[text]
#
- PassInlineMacroRx = /\\?(?:(\+{3}|\${2})(.*?)\1|pass:([a-z,]*)\[(.*?[^\\])\])/m
+ PassInlineMacroRx = /(?:(?:(\\?)\[([^\]]+?)\])?(\\{0,2})(\+{2,3}|\${2})(.*?)\4|(\\?)pass:([a-z,]*)\[(.*?[^\\])\])/m
# Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
#
@@ -1131,26 +1136,25 @@ module Asciidoctor
# constrained quotes:: must be bordered by non-word characters
# NOTE these substitutions are processed in the order they appear here and
# the order in which they are replaced is important
- QUOTE_SUBS = { :default =>
- [
+ default_quote_subs = [
# **strong**
[:strong, :unconstrained, /\\?(?:\[([^\]]+?)\])?\*\*(.+?)\*\*/m],
# *strong*
[:strong, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?\*(\S|\S.*?\S)\*(?!#{CG_WORD})/m],
+ # ``monospaced``
+ [:monospaced, :unconstrained, /\\?(?:\[([^\]]+?)\])?``(.+?)``/m],
+
+ # `monospaced`
+ [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?`(\S|\S.*?\S)`(?!#{CG_WORD})/m],
+
# ``double-quoted''
[:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?``(\S|\S.*?\S)''(?!#{CG_WORD})/m],
# `single-quoted'
[:single, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?`(\S|\S.*?\S)'(?!#{CG_WORD})/m],
- # ++monospaced++
- [:monospaced, :unconstrained, /\\?(?:\[([^\]]+?)\])?\+\+(.+?)\+\+/m],
-
- # +monospaced+
- [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?\+(\S|\S.*?\S)\+(?!#{CG_WORD})/m],
-
# __emphasis__
[:emphasis, :unconstrained, /\\?(?:\[([^\]]+?)\])?__(.+?)__/m],
@@ -1168,10 +1172,25 @@ module Asciidoctor
# ~subscript~
[:subscript, :unconstrained, /\\?(?:\[([^\]]+?)\])?~(\S*?)~/m]
- ]}
+ ]
- QUOTE_SUBS[:legacy] = QUOTE_SUBS[:default].dup.
- insert(3, [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?'(\S|\S.*?\S)'(?!#{CG_WORD})/m])
+ legacy_quote_subs = default_quote_subs.dup
+ # remove +monospaced+ and +monospaced+ to reposition
+ legacy_quote_subs.delete_at 3
+ legacy_quote_subs.delete_at 2
+ # 'emphasis'
+ legacy_quote_subs.insert 3, [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?'(\S|\S.*?\S)'(?!#{CG_WORD})/m]
+ # ++monospaced++
+ legacy_quote_subs.insert 5, [:monospaced, :unconstrained, /\\?(?:\[([^\]]+?)\])?\+\+(.+?)\+\+/m]
+ # +monospaced+
+ legacy_quote_subs.insert 6, [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+?)\])?\+(\S|\S.*?\S)\+(?!#{CG_WORD})/m]
+
+ QUOTE_SUBS = {
+ :default => default_quote_subs,
+ :legacy => legacy_quote_subs,
+ }
+ default_quote_subs = nil
+ legacy_quote_subs = nil
# NOTE in Ruby 1.8.7, [^\\] does not match start of line,
# so we need to match it explicitly
diff --git a/lib/asciidoctor/abstract_node.rb b/lib/asciidoctor/abstract_node.rb
index bee857bc..5dcff9d4 100644
--- a/lib/asciidoctor/abstract_node.rb
+++ b/lib/asciidoctor/abstract_node.rb
@@ -40,7 +40,7 @@ class AbstractNode
@node_name = context.to_s
# QUESTION are we correct in duplicating the attributes (seems to be just as fast)
@attributes = opts.key?(:attributes) ? (opts[:attributes] || {}).dup : {}
- @passthroughs = []
+ @passthroughs = {}
end
# Public: Associate this Block with a new parent Block
diff --git a/lib/asciidoctor/document.rb b/lib/asciidoctor/document.rb
index 206a6efe..8c45e8f3 100644
--- a/lib/asciidoctor/document.rb
+++ b/lib/asciidoctor/document.rb
@@ -183,6 +183,7 @@ class Document < AbstractBlock
SafeMode.const_get(safe_mode.to_s.upcase).to_i rescue SafeMode::SECURE.to_i
end
@sourcemap = options.key?(:sourcemap) && options[:sourcemap] == true
+ @compat_mode = :default
@converter = nil
initialize_extensions = defined? ::Asciidoctor::Extensions
@extensions = nil # initialize furthur down
diff --git a/lib/asciidoctor/substitutors.rb b/lib/asciidoctor/substitutors.rb
index fd08b90e..8482a236 100644
--- a/lib/asciidoctor/substitutors.rb
+++ b/lib/asciidoctor/substitutors.rb
@@ -101,7 +101,7 @@ module Substitutors
if (has_passthroughs = subs.include? :macros)
text = extract_passthroughs text
- has_passthroughs = false if @passthroughs.nil_or_empty?
+ has_passthroughs = false if @passthroughs.empty?
end
subs.each do |type|
@@ -164,57 +164,134 @@ module Substitutors
#
# returns - The text with the passthrough region substituted with placeholders
def extract_passthroughs(text)
+ compat_mode = @document.compat_mode
text = text.gsub(PassInlineMacroRx) {
# alias match for Ruby 1.8.7 compat
m = $~
- # honor the escape
- if m[0].start_with? '\\'
- next m[0][1..-1]
- end
+ preceding = nil
- if m[4].nil_or_empty?
- text = m[2]
- subs = (m[1] == '$$' ? [:specialcharacters] : [])
- else
- text = unescape_brackets m[4]
- if m[3].nil_or_empty?
- subs = []
+ if (boundary = m[4]).nil_or_empty? # pass:[]
+ if m[6] == '\\'
+ # NOTE we don't look for nested pass:[] macros
+ next m[0][1..-1]
+ end
+
+ @passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7].nil_or_empty? ? [] : (resolve_pass_subs m[7]))}
+ else # $$, ++ or +++
+ # skip ++ in legacy mode, handled as normal quoted text
+ if compat_mode == :legacy && boundary == '++'
+ next m[2].nil_or_empty? ?
+ %(#{m[1]}#{m[3]}++#{extract_passthroughs m[5]}++) :
+ %(#{m[1]}[#{m[2]}]#{m[3]}++#{extract_passthroughs m[5]}++)
+ end
+
+ attributes = m[2]
+
+ # fix non-matching group results in Opal under Firefox
+ if ::RUBY_ENGINE_OPAL
+ attributes = nil if attributes == ''
+ end
+
+ escape_count = m[3].size
+ content = m[5]
+ old_behavior = false
+
+ if attributes
+ if escape_count > 0
+ # NOTE we don't look for nested unconstrained pass macros
+ next %(#{m[1]}[#{attributes}]#{'\\' * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
+ elsif m[1] == '\\'
+ preceding = %([#{attributes}])
+ attributes = nil
+ else
+ if boundary == '++' && (attributes.end_with? 'x-')
+ old_behavior = true
+ attributes = attributes[0...-2]
+ end
+ attributes = parse_attributes attributes
+ end
+ elsif escape_count > 0
+ # NOTE we don't look for nested unconstrained pass macros
+ next %(#{m[1]}[#{attributes}]#{'\\' * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
+ end
+ subs = (boundary == '+++' ? [] : [:specialcharacters])
+
+ pass_key = @passthroughs.size
+ if attributes
+ if old_behavior
+ @passthroughs[pass_key] = {:text => content, :subs => SUBS[:normal], :type => :monospaced, :attributes => attributes}
+ else
+ @passthroughs[pass_key] = {:text => content, :subs => subs, :type => :unquoted, :attributes => attributes}
+ end
else
- subs = resolve_pass_subs m[3]
+ @passthroughs[pass_key] = {:text => content, :subs => subs}
end
end
- @passthroughs << {:text => text, :subs => subs}
- index = @passthroughs.size - 1
- %(#{PASS_START}#{index}#{PASS_END})
+ %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
} if (text.include? '++') || (text.include? '$$') || (text.include? 'ss:')
- text = text.gsub(PassInlineLiteralRx) {
+ pass_inline_char1, pass_inline_char2, pass_inline_rx = PassInlineRx[compat_mode]
+ text = text.gsub(pass_inline_rx) {
# alias match for Ruby 1.8.7 compat
m = $~
+ preceding = m[1]
+ attributes = m[2]
+ escape_mark = (m[3].start_with? '\\') ? '\\' : nil
+ format_mark = m[4]
+ content = m[5]
+
# fix non-matching group results in Opal under Firefox
if ::RUBY_ENGINE_OPAL
- m[2] = nil if m[2] == ''
+ attributes = nil if attributes == ''
end
- unescaped_attrs = nil
- # honor the escape
- if m[3].start_with? '\\'
- next m[2] ? %(#{m[1]}[#{m[2]}]#{m[3][1..-1]}) : %(#{m[1]}#{m[3][1..-1]})
- elsif m[1] == '\\' && m[2]
- unescaped_attrs = "[#{m[2]}]"
+ if compat_mode == :default
+ if (old_behavior = (attributes && (attributes.end_with? 'x-')))
+ attributes = attributes[0...-2]
+ end
+ else
+ old_behavior = true
end
- if !unescaped_attrs && m[2]
- attributes = parse_attributes(m[2])
+ if attributes
+ if format_mark == '`' && !old_behavior
+ next %(#{preceding}[#{attributes}]#{escape_mark}`#{extract_passthroughs content}`)
+ end
+
+ if escape_mark
+ # honor the escape of the formatting mark
+ next "#{preceding}[#{attributes}]#{m[3][1..-1]}"
+ elsif preceding == '\\'
+ # honor the escape of the attributes
+ preceding = %([#{attributes}])
+ attributes = nil
+ else
+ attributes = parse_attributes attributes
+ end
+ elsif format_mark == '`' && !old_behavior
+ next %(#{preceding}#{escape_mark}`#{extract_passthroughs content}`)
+ elsif escape_mark
+ # honor the escape of the formatting mark
+ next "#{preceding}#{m[3][1..-1]}"
+ end
+
+ pass_key = @passthroughs.size
+ if compat_mode == :legacy
+ @passthroughs[pass_key] = {:text => content, :subs => [:specialcharacters], :attributes => attributes, :type => :monospaced}
+ elsif attributes
+ if old_behavior
+ subs = (format_mark == '`' ? [:specialcharacters] : SUBS[:normal])
+ @passthroughs[pass_key] = {:text => content, :subs => subs, :attributes => attributes, :type => :monospaced}
+ else
+ @passthroughs[pass_key] = {:text => content, :subs => [:specialcharacters], :attributes => attributes, :type => :unquoted}
+ end
else
- attributes = nil
+ @passthroughs[pass_key] = {:text => content, :subs => [:specialcharacters]}
end
- @passthroughs << {:text => m[4], :subs => [:specialcharacters], :attributes => attributes, :type => :monospaced}
- index = @passthroughs.size - 1
- %(#{unescaped_attrs || m[1]}#{PASS_START}#{index}#{PASS_END})
- } if (text.include? '`')
+ %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
+ } if (text.include? pass_inline_char1) || (pass_inline_char2 && (text.include? pass_inline_char2))
# NOTE we need to do the stem in a subsequent step to allow it to be escaped by the former
text = text.gsub(StemInlineMacroRx) {
@@ -228,16 +305,15 @@ module Substitutors
if (type = m[1].to_sym) == :stem
type = ((default_stem_type = document.attributes['stem']).nil_or_empty? ? 'asciimath' : default_stem_type).to_sym
end
- text = unescape_brackets m[3]
+ content = unescape_brackets m[3]
if m[2].nil_or_empty?
subs = (@document.basebackend? 'html') ? [:specialcharacters] : []
else
subs = resolve_pass_subs m[2]
end
- @passthroughs << {:text => text, :subs => subs, :type => type}
- index = @passthroughs.size - 1
- "#{PASS_START}#{index}#{PASS_END}"
+ @passthroughs[pass_key = @passthroughs.size] = {:text => content, :subs => subs, :type => type}
+ %(#{PASS_START}#{pass_key}#{PASS_END})
} if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
text
@@ -249,22 +325,18 @@ module Substitutors
# check - A Boolean indicating whether to check whether substitution is necessary (default: true)
#
# returns The String text with the passthrough text restored
- def restore_passthroughs(text, check = true)
- return text if check && @passthroughs.nil_or_empty? || !text.include?(PASS_START)
+ def restore_passthroughs text, check = true
+ if check && (@passthroughs.empty? || !text.include?(PASS_START))
+ return text
+ end
text.gsub(PASS_MATCH) {
- pass = @passthroughs[$~[1].to_i]
- subbed_text = (subs = pass[:subs]) ? (apply_subs pass[:text], subs) : pass[:text]
+ pass = @passthroughs.delete $~[1].to_i
+ subbed_text = (subs = pass[:subs]) ? apply_subs(pass[:text], subs) : pass[:text]
if (type = pass[:type])
subbed_text = Inline.new(self, :quoted, subbed_text, :type => type, :attributes => pass[:attributes]).convert
end
- subbed_text
- # TODO enable the following code to restore passthroughs recursively
- #if subbed_text.include? PASS_START
- # restore_passthroughs subbed_text, false
- #else
- # subbed_text
- #end
+ (@passthroughs.empty? || !subbed_text.include?(PASS_START)) ? subbed_text : restore_passthroughs(subbed_text, false)
}
end
@@ -659,7 +731,7 @@ module Substitutors
m = $~
# honor the escape
if m[2].start_with? '\\'
- # NOTE Opal doesn't like %() as an enclosure around this string
+ # NOTE Opal doesn't like %() as an enclosure around this string because of range
next "#{m[1]}#{m[2][1..-1]}#{m[3]}"
end
# fix non-matching group results in Opal under Firefox
@@ -1371,7 +1443,7 @@ module Substitutors
# fix passthrough placeholders that got caught up in syntax highlighting
unless @passthroughs.empty?
- result = result.gsub PASS_MATCH_HI, "#{PASS_START}\\1#{PASS_END}"
+ result = result.gsub PASS_MATCH_HI, %(#{PASS_START}\\1#{PASS_END})
end
if !sub_callouts || callout_marks.empty?
diff --git a/test/attributes_test.rb b/test/attributes_test.rb
index 52eb0eea..22acd83c 100644
--- a/test/attributes_test.rb
+++ b/test/attributes_test.rb
@@ -480,6 +480,15 @@ v1.0, 2010-01-01: First release!
.Require the +{gem_name}+ gem
To use {gem_name}, the first thing to do is to import it in your Ruby source file.
EOS
+ output = render_embedded_string input, :attributes => {'compat-mode' => 'legacy'}
+ assert_xpath '//*[@class="title"]/code[text()="asciidoctor"]', output, 1
+
+ input = <<-EOS
+:gem_name: asciidoctor
+
+.Require the `{gem_name}` gem
+To use {gem_name}, the first thing to do is to import it in your Ruby source file.
+ EOS
output = render_embedded_string input
assert_xpath '//*[@class="title"]/code[text()="asciidoctor"]', output, 1
end
diff --git a/test/extensions_test.rb b/test/extensions_test.rb
index c27105df..9f3cc862 100644
--- a/test/extensions_test.rb
+++ b/test/extensions_test.rb
@@ -415,7 +415,7 @@ last line
# Safe Mode is not required here
document = empty_document :base_dir => File.expand_path(File.dirname(__FILE__))
document.extensions.include_processor do
- process do |document, reader, target, attributes|
+ process do |doc, reader, target, attributes|
# demonstrate that push_include normalizes endlines
content = ["include target:: #{target}\n", "\n", "middle line\n"]
reader.push_include content, target, target, 1, attributes
diff --git a/test/substitutions_test.rb b/test/substitutions_test.rb
index fb144aab..d707ef74 100644
--- a/test/substitutions_test.rb
+++ b/test/substitutions_test.rb
@@ -290,47 +290,86 @@ context 'Substitutions' do
end
test 'single-line constrained monospaced chars' do
- para = block_from_string(%q{call +save()+ to persist the changes})
+ para = block_from_string(%q{call +save()+ to persist the changes}, :attributes => {'compat-mode' => 'legacy'})
+ assert_equal 'call <code>save()</code> to persist the changes', para.sub_quotes(para.source)
+
+ para = block_from_string(%q{call [x-]+save()+ to persist the changes})
+ assert_equal 'call <code>save()</code> to persist the changes', para.apply_subs(para.source)
+
+ para = block_from_string(%q{call `save()` to persist the changes})
assert_equal 'call <code>save()</code> to persist the changes', para.sub_quotes(para.source)
end
test 'single-line constrained monospaced chars with role' do
- para = block_from_string(%q{call [method]+save()+ to persist the changes})
+ para = block_from_string(%q{call [method]+save()+ to persist the changes}, :attributes => {'compat-mode' => 'legacy'})
+ assert_equal 'call <code class="method">save()</code> to persist the changes', para.sub_quotes(para.source)
+
+ para = block_from_string(%q{call [method x-]+save()+ to persist the changes})
+ assert_equal 'call <code class="method">save()</code> to persist the changes', para.apply_subs(para.source)
+
+ para = block_from_string(%q{call [method]`save()` to persist the changes})
assert_equal 'call <code class="method">save()</code> to persist the changes', para.sub_quotes(para.source)
end
test 'escaped single-line constrained monospaced chars' do
- para = block_from_string(%(call #{BACKSLASH}+save()+ to persist the changes))
+ para = block_from_string(%(call #{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => 'legacy'})
assert_equal 'call +save()+ to persist the changes', para.sub_quotes(para.source)
+
+ para = block_from_string(%(call #{BACKSLASH}`save()` to persist the changes))
+ assert_equal 'call `save()` to persist the changes', para.sub_quotes(para.source)
end
test 'escaped single-line constrained monospaced chars with role' do
- para = block_from_string(%(call [method]#{BACKSLASH}+save()+ to persist the changes))
+ para = block_from_string(%(call [method]#{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => 'legacy'})
assert_equal 'call [method]+save()+ to persist the changes', para.sub_quotes(para.source)
+
+ para = block_from_string(%(call [method]#{BACKSLASH}`save()` to persist the changes))
+ assert_equal 'call [method]`save()` to persist the changes', para.sub_quotes(para.source)
end
test 'escaped role on single-line constrained monospaced chars' do
- para = block_from_string(%(call #{BACKSLASH}[method]+save()+ to persist the changes))
+ para = block_from_string(%(call #{BACKSLASH}[method]+save()+ to persist the changes), :attributes => {'compat-mode' => 'legacy'})
+ assert_equal 'call [method]<code>save()</code> to persist the changes', para.sub_quotes(para.source)
+
+ para = block_from_string(%(call #{BACKSLASH}[method]`save()` to persist the changes))
assert_equal 'call [method]<code>save()</code> to persist the changes', para.sub_quotes(para.source)
end
test 'escaped role on escaped single-line constrained monospaced chars' do
- para = block_from_string(%(call #{BACKSLASH}[method]#{BACKSLASH}+save()+ to persist the changes))
+ para = block_from_string(%(call #{BACKSLASH}[method]#{BACKSLASH}+save()+ to persist the changes), :attributes => {'compat-mode' => 'legacy'})
assert_equal %(call #{BACKSLASH}[method]+save()+ to persist the changes), para.sub_quotes(para.source)
+
+ para = block_from_string(%(call #{BACKSLASH}[method]#{BACKSLASH}`save()` to persist the changes))
+ assert_equal %(call #{BACKSLASH}[method]`save()` to persist the changes), para.sub_quotes(para.source)
end
test 'single-line unconstrained monospaced chars' do
- para = block_from_string(%q{Git++Hub++})
+ para = block_from_string(%q{Git++Hub++}, :attributes => {'compat-mode' => 'legacy'})
+ assert_equal 'Git<code>Hub</code>', para.sub_quotes(para.source)
+
+ para = block_from_string(%q{Git[x-]++Hub++})
+ assert_equal 'Git<code>Hub</code>', para.apply_subs(para.source)
+
+ para = block_from_string(%q{Git``Hub``})
assert_equal 'Git<code>Hub</code>', para.sub_quotes(para.source)
end
test 'escaped single-line unconstrained monospaced chars' do
- para = block_from_string(%(Git#{BACKSLASH}++Hub++))
+ para = block_from_string(%(Git#{BACKSLASH}++Hub++), :attributes => {'compat-mode' => 'legacy'})
assert_equal 'Git+<code>Hub</code>+', para.sub_quotes(para.source)
+
+ para = block_from_string(%(Git#{BACKSLASH}``Hub``))
+ assert_equal 'Git`<code>Hub</code>`', para.sub_quotes(para.source)
end
test 'multi-line unconstrained monospaced chars' do
- para = block_from_string(%Q{Git++\nH\nu\nb++})
+ para = block_from_string(%Q{Git++\nH\nu\nb++}, :attributes => {'compat-mode' => 'legacy'})
+ assert_equal "Git<code>\nH\nu\nb</code>", para.sub_quotes(para.source)
+
+ para = block_from_string(%Q{Git[x-]++\nH\nu\nb++})
+ assert_equal %(Git<code>\nH\nu\nb</code>), para.apply_subs(para.source)
+
+ para = block_from_string(%Q{Git``\nH\nu\nb``})
assert_equal "Git<code>\nH\nu\nb</code>", para.sub_quotes(para.source)
end
@@ -1046,8 +1085,8 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal '<code>inline code</code>', para.passthroughs.first[:text]
- assert para.passthroughs.first[:subs].empty?
+ assert_equal '<code>inline code</code>', para.passthroughs[0][:text]
+ assert para.passthroughs[0][:subs].empty?
end
test 'collect multi-line inline triple plus passthroughs' do
@@ -1055,8 +1094,8 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal "<code>inline\ncode</code>", para.passthroughs.first[:text]
- assert para.passthroughs.first[:subs].empty?
+ assert_equal "<code>inline\ncode</code>", para.passthroughs[0][:text]
+ assert para.passthroughs[0][:subs].empty?
end
test 'collect inline double dollar passthroughs' do
@@ -1064,8 +1103,17 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal '<code>{code}</code>', para.passthroughs.first[:text]
- assert_equal [:specialcharacters], para.passthroughs.first[:subs]
+ assert_equal '<code>{code}</code>', para.passthroughs[0][:text]
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
+ end
+
+ test 'collect inline double plus passthroughs' do
+ para = block_from_string('++<code>{code}</code>++')
+ result = para.extract_passthroughs(para.source)
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
+ assert_equal 1, para.passthroughs.size
+ assert_equal '<code>{code}</code>', para.passthroughs[0][:text]
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
end
test 'collect multi-line inline double dollar passthroughs' do
@@ -1073,8 +1121,17 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal "<code>\n{code}\n</code>", para.passthroughs.first[:text]
- assert_equal [:specialcharacters], para.passthroughs.first[:subs]
+ assert_equal "<code>\n{code}\n</code>", para.passthroughs[0][:text]
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
+ end
+
+ test 'collect multi-line inline double plus passthroughs' do
+ para = block_from_string("++<code>\n{code}\n</code>++")
+ result = para.extract_passthroughs(para.source)
+ assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
+ assert_equal 1, para.passthroughs.size
+ assert_equal "<code>\n{code}\n</code>", para.passthroughs[0][:text]
+ assert_equal [:specialcharacters], para.passthroughs[0][:subs]
end
test 'collect passthroughs from inline pass macro' do
@@ -1082,8 +1139,8 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal %q{<code>['code']</code>}, para.passthroughs.first[:text]
- assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
+ assert_equal %q{<code>['code']</code>}, para.passthroughs[0][:text]
+ assert_equal [:specialcharacters, :quotes], para.passthroughs[0][:subs]
end
test 'collect multi-line passthroughs from inline pass macro' do
@@ -1091,15 +1148,15 @@ EOS
result = para.extract_passthroughs(para.source)
assert_equal Asciidoctor::Substitutors::PASS_START + '0' + Asciidoctor::Substitutors::PASS_END, result
assert_equal 1, para.passthroughs.size
- assert_equal %Q{<code>['more\ncode']</code>}, para.passthroughs.first[:text]
- assert_equal [:specialcharacters, :quotes], para.passthroughs.first[:subs]
+ assert_equal %Q{<code>['more\ncode']</code>}, para.passthroughs[0][:text]
+ assert_equal [:specialcharacters, :quotes], para.passthroughs[0][:subs]
end
test 'resolves sub shorthands on inline pass macro' do
para = block_from_string 'pass:q,a[*<{backend}>*]'
result = para.extract_passthroughs para.source
assert_equal 1, para.passthroughs.size
- assert_equal [:quotes, :attributes], para.passthroughs.first[:subs]
+ assert_equal [:quotes, :attributes], para.passthroughs[0][:subs]
result = para.restore_passthroughs result
assert_equal '<strong><html5></strong>', result
end
@@ -1107,7 +1164,7 @@ EOS
# NOTE placeholder is surrounded by text to prevent reader from stripping trailing boundary char (unique to test scenario)
test 'restore inline passthroughs without subs' do
para = block_from_string("some #{Asciidoctor::Substitutors::PASS_START}" + '0' + "#{Asciidoctor::Substitutors::PASS_END} to study")
- para.passthroughs << {:text => '<code>inline code</code>', :subs => []}
+ para.passthroughs[0] = {:text => '<code>inline code</code>', :subs => []}
result = para.restore_passthroughs(para.source)
assert_equal "some <code>inline code</code> to study", result
end
@@ -1115,12 +1172,22 @@ EOS
# NOTE placeholder is surrounded by text to prevent reader from stripping trailing boundary char (unique to test scenario)
test 'restore inline passthroughs with subs' do
para = block_from_string("some #{Asciidoctor::Substitutors::PASS_START}" + '0' + "#{Asciidoctor::Substitutors::PASS_END} to study in the #{Asciidoctor::Substitutors::PASS_START}" + '1' + "#{Asciidoctor::Substitutors::PASS_END} programming language")
- para.passthroughs << {:text => '<code>{code}</code>', :subs => [:specialcharacters]}
- para.passthroughs << {:text => '{language}', :subs => [:specialcharacters]}
+ para.passthroughs[0] = {:text => '<code>{code}</code>', :subs => [:specialcharacters]}
+ para.passthroughs[1] = {:text => '{language}', :subs => [:specialcharacters]}
result = para.restore_passthroughs(para.source)
assert_equal 'some &lt;code&gt;{code}&lt;/code&gt; to study in the {language} programming language', result
end
+ test 'restore nested passthroughs' do
+ result = render_embedded_string %q(+I'm in pass:q[`mono`].+), :doctype => :inline
+ assert_equal %q(I'm in <code>mono</code>.), result
+ end
+
+ test 'should honor role on double plus passthrough' do
+ result = render_embedded_string 'Print the version using [var]++{asciidoctor-version}++.', :doctype => :inline
+ assert_equal 'Print the version using <span class="var">{asciidoctor-version}</span>.', result
+ end
+
test 'complex inline passthrough macro' do
text_to_escape = %q{[(] <'basic form'> <'logical operator'> <'basic form'> [)]}
para = block_from_string %($$#{text_to_escape}$$)
@@ -1201,9 +1268,17 @@ EOS
assert_equal '\(\sqrt{4} = 2\)', para.content
end
- test 'should passthrough asciimath macro inside another passthrough' do
- input = 'the text `asciimath:[x = y]` should be passed through as `literal` text'
- para = block_from_string input
+ test 'should passthrough math macro inside another passthrough' do
+ input = 'the text `asciimath:[x = y]` should be passed through as +literal+ text'
+ para = block_from_string input, :attributes => {'compat-mode' => 'legacy'}
+ assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
+
+ input = 'the text [x-]`asciimath:[x = y]` should be passed through as `literal` text'
+ para = block_from_string input
+ assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
+
+ input = 'the text `+asciimath:[x = y]+` should be passed through as `literal` text'
+ para = block_from_string input
assert_equal 'the text <code>asciimath:[x = y]</code> should be passed through as <code>literal</code> text', para.content
end
diff --git a/test/text_test.rb b/test/text_test.rb
index cc675845..6e40346f 100644
--- a/test/text_test.rb
+++ b/test/text_test.rb
@@ -214,48 +214,69 @@ This line is separated something that is not a horizontal rule...
refute_match(/#/, render_string("An #unquoted# word"))
end
- test "backtick-escaped text followed by single-quoted text" do
+ test "backtick text followed by single-quoted text" do
assert_match(/<code>foo<\/code>/, render_string(%Q(run `foo` 'dog')))
end
+ test 'plus characters inside single plus passthrough' do
+ assert_xpath '//p[text()="+"]', render_embedded_string('+++')
+ assert_xpath '//p[text()="+="]', render_embedded_string('++=+')
+ end
+
+ test 'plus passthrough escapes entity reference' do
+ assert_match(/&amp;#44;/, render_embedded_string('+&#44;+'))
+ assert_match(/one&amp;#44;two/, render_embedded_string('one++&#44;++two'))
+ end
+
context "basic styling" do
setup do
- @rendered = render_string("A *BOLD* word. An _italic_ word. A +mono+ word. ^superscript!^ and some ~subscript~.")
+ @rendered = render_string("A *BOLD* word. An _italic_ word. A `mono` word. ^superscript!^ and some ~subscript~.")
end
test "strong" do
- assert_xpath "//strong", @rendered
+ assert_xpath "//strong", @rendered, 1
end
test "italic" do
- assert_xpath "//em", @rendered
+ assert_xpath "//em", @rendered, 1
end
test "monospaced" do
- assert_xpath "//code", @rendered
+ assert_xpath "//code", @rendered, 1
end
test "superscript" do
- assert_xpath "//sup", @rendered
+ assert_xpath "//sup", @rendered, 1
end
test "subscript" do
- assert_xpath "//sub", @rendered
+ assert_xpath "//sub", @rendered, 1
end
- test "backticks" do
- assert_xpath "//code", render_string("This is `totally cool`.")
+ test "passthrough" do
+ assert_xpath "//code", render_string("This is +passed through+."), 0
+ assert_xpath "//code", render_string("This is +passed through and monospaced+.", :attributes => {'compat-mode' => 'legacy'}), 1
end
test "nested styles" do
- rendered = render_string("Winning *big _time_* in the +city *boyeeee*+.")
+ rendered = render_string("Winning *big _time_* in the +city *boyeeee*+.", :attributes => {'compat-mode' => 'legacy'})
+
+ assert_xpath "//strong/em", rendered
+ assert_xpath "//code/strong", rendered
+
+ rendered = render_string("Winning *big _time_* in the `city *boyeeee*`.")
assert_xpath "//strong/em", rendered
assert_xpath "//code/strong", rendered
end
test "unconstrained quotes" do
- rendered_chars = render_string("**B**__I__++M++")
+ rendered_chars = render_string("**B**__I__++M++", :attributes => {'compat-mode' => 'legacy'})
+ assert_xpath "//strong", rendered_chars
+ assert_xpath "//em", rendered_chars
+ assert_xpath "//code", rendered_chars
+
+ rendered_chars = render_string("**B**__I__``M``")
assert_xpath "//strong", rendered_chars
assert_xpath "//em", rendered_chars
assert_xpath "//code", rendered_chars