diff --git a/CHANGELOG.md b/CHANGELOG.md index a1b78b3..324134e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## Unreleased + +- 🐛 FIX: front matter closed by `...` is parsed correctly + + The YAML document-end marker was tested against the previous line rather than + the current one, because `start`/`maximum` were only moved onto the current + line after the test. Two things followed: a block whose last line was `...` was + dropped entirely, so the front matter leaked into the document as visible text, + and where it did parse, the marker itself was included in the token content. + + ```python + MarkdownIt().use(front_matter_plugin).render("---\ntitle: a\n...\n") + # before: '
title: a\n...
\n' + # after: '' + ``` + ## 0.7.0 - 2026-07-19 - ✨ NEW: Add section reference plugin (`section_ref`) (#144) diff --git a/mdit_py_plugins/front_matter/index.py b/mdit_py_plugins/front_matter/index.py index 1551d76..3e8ee9b 100644 --- a/mdit_py_plugins/front_matter/index.py +++ b/mdit_py_plugins/front_matter/index.py @@ -69,12 +69,17 @@ 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.src[start:maximum] == "...": + # The YAML document-end marker closes the block on this line, so it must + # be consumed here: checking it before start/maximum are moved onto the + # current line tests the previous one, which both missed a `...` that + # ends the document and leaked the marker into the extracted 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/test_front_matter.py b/tests/test_front_matter.py index 627cb89..8401e75 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)