diff --git a/mdit_py_plugins/front_matter/index.py b/mdit_py_plugins/front_matter/index.py
index 1551d76..28a176e 100644
--- a/mdit_py_plugins/front_matter/index.py
+++ b/mdit_py_plugins/front_matter/index.py
@@ -69,12 +69,19 @@ def _front_matter_rule(
# unclosed block should be autoclosed by end of document.
return False
- if state.src[start:maximum] == "...":
- break
-
start = state.bMarks[nextLine] + state.tShift[nextLine]
maximum = state.eMarks[nextLine]
+ if (
+ state.sCount[nextLine] == 0
+ and state.src.startswith("...", start)
+ and state.skipSpaces(start + 3) >= maximum
+ ):
+ # YAML document-end marker: `...` at column 0, trailing spaces allowed.
+ # It is checked on the current line and consumed, so it is not content.
+ auto_closed = True
+ break
+
if start < maximum and state.sCount[nextLine] < state.blkIndent:
# non-empty line with negative indent should stop the list:
# - ```
diff --git a/tests/fixtures/front_matter.md b/tests/fixtures/front_matter.md
index e01fd4e..5cff35b 100644
--- a/tests/fixtures/front_matter.md
+++ b/tests/fixtures/front_matter.md
@@ -32,6 +32,61 @@ x: 1
Head
.
+should parse triple dots at end of document:
+.
+---
+x: 1
+...
+.
+.
+
+should ignore spaces after triple dots:
+.
+---
+x: 1
+...
+# Head
+.
+
+Head
+.
+
+should not close on indented triple dots:
+.
+---
+text: |
+ ...
+---
+# Head
+.
+
+Head
+.
+
+should not close on four dots:
+.
+---
+x: 1
+....
+---
+# Head
+.
+
+Head
+.
+
+should not close on triple dots followed by text:
+.
+---
+x: 1
+...x
+---
+# Head
+.
+
+Head
+.
+
should parse front matter with indentation:
.
---
diff --git a/tests/test_front_matter.py b/tests/test_front_matter.py
index 627cb89..00b714c 100644
--- a/tests/test_front_matter.py
+++ b/tests/test_front_matter.py
@@ -42,6 +42,30 @@ def test_token():
]
+@pytest.mark.parametrize("closer", ["---", "...", "... "])
+def test_token_content_excludes_the_closer(closer):
+ """The closing marker must not be left inside the extracted content.
+
+ ``...`` is the YAML document-end marker and is exercised by the fixture file,
+ so it has to be consumed like the dashed form rather than kept in the block.
+ """
+ md = MarkdownIt("commonmark").use(front_matter_plugin)
+ tokens = md.parse(f"---\na: 1\n{closer}")
+ assert [token.type for token in tokens] == ["front_matter"]
+ assert tokens[0].content == "a: 1"
+
+
+def test_dots_closer_on_the_last_line():
+ """A block closed by ``...`` on the final line is still front matter.
+
+ ``test_all`` cannot see this: the front_matter token is hidden, so only the
+ rendered output is asserted, and a block that is dropped rather than closed
+ renders as visible text instead of disappearing.
+ """
+ md = MarkdownIt("commonmark").use(front_matter_plugin)
+ assert md.render("---\na: 1\n...\n") == md.render("---\na: 1\n---\n") == ""
+
+
def test_short_source():
md = MarkdownIt("commonmark").use(front_matter_plugin)