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)