diff --git a/lib/asciidoctor/rx.rb b/lib/asciidoctor/rx.rb
index f571e3f79e..cceca56175 100644
--- a/lib/asciidoctor/rx.rb
+++ b/lib/asciidoctor/rx.rb
@@ -469,7 +469,7 @@ module Rx; end
# footnoteref:[id,text] (legacy)
# footnoteref:[id] (legacy)
#
- InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\](?!<\/a>)/m
+ InlineFootnoteMacroRx = /\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|((([^\]]|\\\])*\g<0>)?#{CC_ALL}*?[^\\]))\](?!<\/a>)/m
# Matches an image or icon inline macro.
#
diff --git a/lib/asciidoctor/substitutors.rb b/lib/asciidoctor/substitutors.rb
index 47ffc5e7d8..5f45f797ee 100644
--- a/lib/asciidoctor/substitutors.rb
+++ b/lib/asciidoctor/substitutors.rb
@@ -286,6 +286,62 @@ def sub_replacements text
text
end
+ # Public: Substitute replacement footnote
+ #
+ # text - The String text to process with footnote
+ #
+ # returns the [String] text with footnote substituted
+ def sub_footnote text
+ doc = @document
+ text = text.gsub InlineFootnoteMacroRx do
+ # honor the escape
+ next $&.slice 1, $&.length if $&.start_with? RS
+
+ # footnoteref
+ if $1
+ if $3
+ id, text = $3.split ',', 2
+ logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
+ else
+ next $&
+ end
+ # footnote
+ else
+ id = $2
+ text = $3
+ end
+
+ fn = nil
+ if id
+ if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
+ index, text = footnote.index, footnote.text
+ type, target, id = :xref, id, nil
+ elsif text
+ text = restore_passthroughs(normalize_text text, true, true)
+ index = doc.counter('footnote-number')
+ fn = Document::Footnote.new(index, id, text)
+ doc.register(:footnotes, fn)
+ type, target = :ref, nil
+ else
+ logger.warn %(invalid footnote reference: #{id})
+ type, target, text, id = :xref, id, id, nil
+ end
+ elsif text
+ text = restore_passthroughs(normalize_text text, true, true)
+ index = doc.counter('footnote-number')
+ fn = Document::Footnote.new(index, id, text)
+ doc.register(:footnotes, fn)
+ type = target = nil
+ else
+ next $&
+ end
+ if fn
+ fn.text = sub_footnote fn.text
+ end
+ Inline.new(self, :footnote, text, attributes: { 'index' => index }, id: id, target: target, type: type).convert
+ end
+ end
+
# Public: Substitute inline macros (e.g., links, images, etc)
#
# Replace inline macros, which may span multiple lines, in the provided text
@@ -830,47 +886,7 @@ def sub_macros text
end
if found_macroish && (text.include? 'tnote')
- text = text.gsub InlineFootnoteMacroRx do
- # honor the escape
- next $&.slice 1, $&.length if $&.start_with? RS
-
- # footnoteref
- if $1
- if $3
- id, text = $3.split ',', 2
- logger.warn %(found deprecated footnoteref macro: #{$&}; use footnote macro with target instead) unless doc.compat_mode
- else
- next $&
- end
- # footnote
- else
- id = $2
- text = $3
- end
-
- if id
- if (footnote = doc.footnotes.find {|candidate| candidate.id == id })
- index, text = footnote.index, footnote.text
- type, target, id = :xref, id, nil
- elsif text
- text = restore_passthroughs(normalize_text text, true, true)
- index = doc.counter('footnote-number')
- doc.register(:footnotes, Document::Footnote.new(index, id, text))
- type, target = :ref, nil
- else
- logger.warn %(invalid footnote reference: #{id})
- type, target, text, id = :xref, id, id, nil
- end
- elsif text
- text = restore_passthroughs(normalize_text text, true, true)
- index = doc.counter('footnote-number')
- doc.register(:footnotes, Document::Footnote.new(index, id, text))
- type = target = nil
- else
- next $&
- end
- Inline.new(self, :footnote, text, attributes: { 'index' => index }, id: id, target: target, type: type).convert
- end
+ text = sub_footnote text
end
text
diff --git a/test/substitutions_test.rb b/test/substitutions_test.rb
index 858796c427..63507ea689 100644
--- a/test/substitutions_test.rb
+++ b/test/substitutions_test.rb
@@ -2298,5 +2298,35 @@
block.commit_subs
assert_equal [:specialcharacters, :quotes, :attributes, :replacements, :macros, :post_replacements], block.subs
end
+
+ test 'should parse footnote inside footnote' do
+ para = block_from_string('Sentence text footnote:[An examplefootnote:[Footnote to footnote.] footnote.].')
+ assert_equal %(Sentence text .), para.sub_macros(para.source)
+ assert_equal 2, para.document.catalog[:footnotes].size
+ footnote = para.document.catalog[:footnotes][0]
+ assert_equal 1, footnote.index
+ assert_nil footnote.id
+ assert_equal 'An example footnote.', footnote.text
+
+ footnote = para.document.catalog[:footnotes][1]
+ assert_equal 2, footnote.index
+ assert_nil footnote.id
+ assert_equal 'Footnote to footnote.', footnote.text
+ end
+
+ test 'should accept escaped square bracket before footnote inside footnote' do
+ para = block_from_string('Sentence text footnote:[An \] examplefootnote:[Footnote to footnote.] footnote.].')
+ assert_equal %(Sentence text .), para.sub_macros(para.source)
+ assert_equal 2, para.document.catalog[:footnotes].size
+ footnote = para.document.catalog[:footnotes][0]
+ assert_equal 1, footnote.index
+ assert_nil footnote.id
+ assert_equal 'An ] example footnote.', footnote.text
+
+ footnote = para.document.catalog[:footnotes][1]
+ assert_equal 2, footnote.index
+ assert_nil footnote.id
+ assert_equal 'Footnote to footnote.', footnote.text
+ end
end
end