diff --git a/complex-paragraphs/Makefile b/complex-paragraphs/Makefile
new file mode 100644
index 00000000..081c086b
--- /dev/null
+++ b/complex-paragraphs/Makefile
@@ -0,0 +1,12 @@
+.PHONY: test
+
+PANDOC_COMMAND = pandoc -L complex-paragraphs.lua -t native sample.md
+
+test:
+ TESTED_FORMAT=context $(PANDOC_COMMAND) -o tests/output.context \
+ && diff tests/output.context tests/expected.context
+ TESTED_FORMAT=openxml $(PANDOC_COMMAND) -o tests/output.docx \
+ && diff tests/output.docx tests/expected.docx
+ TESTED_FORMAT=latex $(PANDOC_COMMAND) -o tests/output.latex \
+ && diff tests/output.latex tests/expected.latex \
+ && rm tests/output.*
diff --git a/complex-paragraphs/README.md b/complex-paragraphs/README.md
new file mode 100644
index 00000000..868020eb
--- /dev/null
+++ b/complex-paragraphs/README.md
@@ -0,0 +1,44 @@
+# Compose complex paragraphs
+
+## Definition
+
+Complex paragraphs are paragraphs composed of different blocks:
+normal text, quotations, tables,...
+
+This concept makes sense only if you want to indent all paragraphs
+by default, including paragraphs beginning after a quotation block
+or a table, for instance. In that case, unindenting a text block means
+that it is not to be seen as a new paragraph, but as a the
+continuation of the previous text block that has been interrupted by
+another block. If you want to prevent the indentation of all
+paragraphs following certain types of blocks, please consider using
+the [first-line-indent] filter instead.
+
+[first-line-indent]: https://github.com/pandoc/lua-filters/tree/master/first-line-indent
+
+## How to use this filter
+
+To create a complex paragraph in your MD file, simply wrap its
+components in a Div with class `.complex-paragraph`. Some
+examples are given in `sample.md`.
+
+## What it does
+
+For the moment, it only prevents the indentation of text blocks
+other than the first one. More features can be requested.
+
+The Div itself is not removed from the AST, so that you can
+pass it through other filters.
+
+## Output formats
+
+The following output formats are supported:
+
+ * context
+ * docx
+ * latex
+
+Other formats can be added. PRs are welcome. If you prefer to
+submit an issue instead, please specify what code should be
+used in the targeted format in order to achieve what this filter
+does.
diff --git a/complex-paragraphs/complex-paragraphs.lua b/complex-paragraphs/complex-paragraphs.lua
new file mode 100644
index 00000000..02e604a6
--- /dev/null
+++ b/complex-paragraphs/complex-paragraphs.lua
@@ -0,0 +1,75 @@
+--[[
+complex-paragraphs – compose complex paragraphs
+
+Copyright © 2021 Bastien Dumont
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+]]
+
+local is_after_first_block
+local RAW_ATTRIBUTE
+
+if FORMAT == 'native' then
+ RAW_ATTRIBUTE = pandoc.system.environment().TESTED_FORMAT
+elseif FORMAT == 'docx' then
+ RAW_ATTRIBUTE = 'openxml'
+elseif FORMAT == 'context' or FORMAT == 'latex' then
+ RAW_ATTRIBUTE = FORMAT
+else
+ error(FORMAT ..
+ ' output not supported by complex-paragraphs.lua\n')
+end
+
+local function make_noindent_code()
+ if RAW_ATTRIBUTE == 'context' then
+ return '\\noindentation{}'
+ elseif RAW_ATTRIBUTE == 'openxml' then
+ return '' ..
+ '' ..
+ '' ..
+ ''
+ elseif RAW_ATTRIBUTE == 'latex' then
+ return '\\noindent{}'
+ end
+end
+
+local noindent_rawinline =
+ pandoc.RawInline(RAW_ATTRIBUTE, make_noindent_code())
+
+local function turn_to_nonindented_textblock(para)
+ para.c:insert(1, noindent_rawinline)
+end
+
+local function unindent_paragraphs_after_first_block_in_complex_para(blocks)
+ for i = 1, #blocks do
+ block = blocks[i]
+ if block.t == 'Para' and is_after_first_block then
+ turn_to_nonindented_textblock(block)
+ elseif block.t == 'Div' then
+ unindent_paragraphs_after_first_block_in_complex_para(block.content)
+ end
+ is_after_first_block = true
+ end
+end
+
+local function turn_to_complex_paragraph(div)
+ unindent_paragraphs_after_first_block_in_complex_para(div.content)
+end
+
+function Div(div)
+ if div.classes[1] == 'complex-paragraph' then
+ is_after_first_block = false
+ turn_to_complex_paragraph(div)
+ return div
+ end
+end
diff --git a/complex-paragraphs/sample.md b/complex-paragraphs/sample.md
new file mode 100644
index 00000000..331162a9
--- /dev/null
+++ b/complex-paragraphs/sample.md
@@ -0,0 +1,36 @@
+_Let's begin with a simple test case:_
+
+::: {.complex-paragraph}
+When Clovis came in front of the soldier, he smashed his head with
+his axe, saying:
+
+ > Remember the vase at Soissons!
+
+This was an allusion to the vase that the soldier had broken so that
+it could not be returned to Remigius.
+:::
+
+_It is not necessary for the first block to be normal text:_
+
+::: {.complex-paragraph}
+ > Remember the vase at Soissons!
+
+This was an allusion to the vase that the soldier had broken so that
+it could not be returned to Remigius.
+:::
+
+_Nested Divs are supported:_
+
+::: {.complex-paragraph}
+::: {.narrative}
+When Clovis came in front of the soldier, he smashed his head with
+his axe, saying:
+
+ > Remember the vase at Soissons!
+:::
+::: {.explanation}
+This was an allusion to the vase that the soldier had broken so that
+it could not be returned to Remigius.
+:::
+:::
+
diff --git a/complex-paragraphs/tests/expected.context b/complex-paragraphs/tests/expected.context
new file mode 100644
index 00000000..0c63972b
--- /dev/null
+++ b/complex-paragraphs/tests/expected.context
@@ -0,0 +1,19 @@
+[Para [Emph [Str "Let\8217s",Space,Str "begin",Space,Str "with",Space,Str "a",Space,Str "simple",Space,Str "test",Space,Str "case:"]]
+,Div ("",["complex-paragraph"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "context") "\\noindentation{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "It",Space,Str "is",Space,Str "not",Space,Str "necessary",Space,Str "for",Space,Str "the",Space,Str "first",Space,Str "block",Space,Str "to",Space,Str "be",Space,Str "normal",Space,Str "text:"]]
+,Div ("",["complex-paragraph"],[])
+ [BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "context") "\\noindentation{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "Nested",Space,Str "Divs",Space,Str "are",Space,Str "supported:"]]
+,Div ("",["complex-paragraph"],[])
+ [Div ("",["narrative"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]]
+ ,Div ("",["explanation"],[])
+ [Para [RawInline (Format "context") "\\noindentation{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]]]
diff --git a/complex-paragraphs/tests/expected.docx b/complex-paragraphs/tests/expected.docx
new file mode 100644
index 00000000..62eb4ff1
--- /dev/null
+++ b/complex-paragraphs/tests/expected.docx
@@ -0,0 +1,19 @@
+[Para [Emph [Str "Let\8217s",Space,Str "begin",Space,Str "with",Space,Str "a",Space,Str "simple",Space,Str "test",Space,Str "case:"]]
+,Div ("",["complex-paragraph"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "openxml") "",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "It",Space,Str "is",Space,Str "not",Space,Str "necessary",Space,Str "for",Space,Str "the",Space,Str "first",Space,Str "block",Space,Str "to",Space,Str "be",Space,Str "normal",Space,Str "text:"]]
+,Div ("",["complex-paragraph"],[])
+ [BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "openxml") "",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "Nested",Space,Str "Divs",Space,Str "are",Space,Str "supported:"]]
+,Div ("",["complex-paragraph"],[])
+ [Div ("",["narrative"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]]
+ ,Div ("",["explanation"],[])
+ [Para [RawInline (Format "openxml") "",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]]]
diff --git a/complex-paragraphs/tests/expected.latex b/complex-paragraphs/tests/expected.latex
new file mode 100644
index 00000000..b6773f01
--- /dev/null
+++ b/complex-paragraphs/tests/expected.latex
@@ -0,0 +1,19 @@
+[Para [Emph [Str "Let\8217s",Space,Str "begin",Space,Str "with",Space,Str "a",Space,Str "simple",Space,Str "test",Space,Str "case:"]]
+,Div ("",["complex-paragraph"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "latex") "\\noindent{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "It",Space,Str "is",Space,Str "not",Space,Str "necessary",Space,Str "for",Space,Str "the",Space,Str "first",Space,Str "block",Space,Str "to",Space,Str "be",Space,Str "normal",Space,Str "text:"]]
+,Div ("",["complex-paragraph"],[])
+ [BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]
+ ,Para [RawInline (Format "latex") "\\noindent{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]
+,Para [Emph [Str "Nested",Space,Str "Divs",Space,Str "are",Space,Str "supported:"]]
+,Div ("",["complex-paragraph"],[])
+ [Div ("",["narrative"],[])
+ [Para [Str "When",Space,Str "Clovis",Space,Str "came",Space,Str "in",Space,Str "front",Space,Str "of",Space,Str "the",Space,Str "soldier,",Space,Str "he",Space,Str "smashed",Space,Str "his",Space,Str "head",Space,Str "with",SoftBreak,Str "his",Space,Str "axe,",Space,Str "saying:"]
+ ,BlockQuote
+ [Para [Str "Remember",Space,Str "the",Space,Str "vase",Space,Str "at",Space,Str "Soissons!"]]]
+ ,Div ("",["explanation"],[])
+ [Para [RawInline (Format "latex") "\\noindent{}",Str "This",Space,Str "was",Space,Str "an",Space,Str "allusion",Space,Str "to",Space,Str "the",Space,Str "vase",Space,Str "that",Space,Str "the",Space,Str "soldier",Space,Str "had",Space,Str "broken",Space,Str "so",Space,Str "that",SoftBreak,Str "it",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "returned",Space,Str "to",Space,Str "Remigius."]]]]