From f6f905c4498f513979e05183d2285058d39f53f2 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 20:43:50 -0600 Subject: [PATCH 1/8] test(#56): add red-test --- tests/format/fixtures/regression.md | 6 ++++++ tests/format/test_format.py | 1 + 2 files changed, 7 insertions(+) create mode 100644 tests/format/fixtures/regression.md diff --git a/tests/format/fixtures/regression.md b/tests/format/fixtures/regression.md new file mode 100644 index 0000000..8a7cee8 --- /dev/null +++ b/tests/format/fixtures/regression.md @@ -0,0 +1,6 @@ +Prevent regression with non-deflists: https://github.com/KyleKing/mdformat-mkdocs/issues/56 +. +::: my_lib.core +. +::: my_lib.core +. diff --git a/tests/format/test_format.py b/tests/format/test_format.py index 210fbd2..4bedd77 100644 --- a/tests/format/test_format.py +++ b/tests/format/test_format.py @@ -27,6 +27,7 @@ def flatten(nested_list: list[list[T]]) -> list[T]: "pymd_abbreviations.md", "pymd_snippet.md", "python_markdown_attr_list.md", + "regression.md", "text.md", ) ], From 0a11de3137145e9941b2fd3de283e0c0d70234a1 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 20:45:03 -0600 Subject: [PATCH 2/8] feat: copy deflist plugin from mdit_py_plugins https://github.com/executablebooks/mdit-py-plugins/blob/93fbf645108394bb10ecd392383e96c4e8902f30/mdit_py_plugins/deflist/index.py --- .../mdit_plugins/_material_deflist.py | 2 +- .../mdit_plugins/_mdit_py_plugins/__init__.py | 0 .../mdit_plugins/_mdit_py_plugins/deflist.py | 253 ++++++++++++++++++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py create mode 100644 mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index e27e764..9231d76 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -31,7 +31,7 @@ from typing import TYPE_CHECKING from markdown_it import MarkdownIt -from mdit_py_plugins.deflist import deflist_plugin +from ._mdit_py_plugins.deflist import deflist_plugin if TYPE_CHECKING: from markdown_it import MarkdownIt diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py new file mode 100644 index 0000000..f5d20d1 --- /dev/null +++ b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py @@ -0,0 +1,253 @@ +"""Process definition lists.""" + +from markdown_it import MarkdownIt +from markdown_it.rules_block import StateBlock + +from mdit_py_plugins.utils import is_code_block + + +def deflist_plugin(md: MarkdownIt) -> None: + """Plugin ported from + `markdown-it-deflist `__. + + The syntax is based on + `pandoc definition lists `__: + + .. code-block:: md + + Term 1 + : Definition 1 long form + + second paragraph + + Term 2 with *inline markup* + ~ Definition 2a compact style + ~ Definition 2b + + """ + + def skipMarker(state: StateBlock, line: int) -> int: + """Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail.""" + start = state.bMarks[line] + state.tShift[line] + maximum = state.eMarks[line] + + if start >= maximum: + return -1 + + # Check bullet + marker = state.src[start] + start += 1 + if marker != "~" and marker != ":": + return -1 + + pos = state.skipSpaces(start) + + # require space after ":" + if start == pos: + return -1 + + # no empty definitions, e.g. " : " + if pos >= maximum: + return -1 + + return start + + def markTightParagraphs(state: StateBlock, idx: int) -> None: + level = state.level + 2 + + i = idx + 2 + l2 = len(state.tokens) - 2 + while i < l2: + if ( + state.tokens[i].level == level + and state.tokens[i].type == "paragraph_open" + ): + state.tokens[i + 2].hidden = True + state.tokens[i].hidden = True + i += 2 + i += 1 + + def deflist(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool: + if is_code_block(state, startLine): + return False + + if silent: + # quirk: validation mode validates a dd block only, not a whole deflist + if state.ddIndent < 0: + return False + return skipMarker(state, startLine) >= 0 + + nextLine = startLine + 1 + if nextLine >= endLine: + return False + + if state.isEmpty(nextLine): + nextLine += 1 + if nextLine >= endLine: + return False + + if state.sCount[nextLine] < state.blkIndent: + return False + contentStart = skipMarker(state, nextLine) + if contentStart < 0: + return False + + # Start list + listTokIdx = len(state.tokens) + tight = True + + token = state.push("dl_open", "dl", 1) + token.map = listLines = [startLine, 0] + + # Iterate list items + dtLine = startLine + ddLine = nextLine + + # One definition list can contain multiple DTs, + # and one DT can be followed by multiple DDs. + # + # Thus, there is two loops here, and label is + # needed to break out of the second one + # + break_outer = False + + while True: + prevEmptyEnd = False + + token = state.push("dt_open", "dt", 1) + token.map = [dtLine, dtLine] + + token = state.push("inline", "", 0) + token.map = [dtLine, dtLine] + token.content = state.getLines( + dtLine, dtLine + 1, state.blkIndent, False + ).strip() + token.children = [] + + token = state.push("dt_close", "dt", -1) + + while True: + token = state.push("dd_open", "dd", 1) + token.map = itemLines = [nextLine, 0] + + pos = contentStart + maximum = state.eMarks[ddLine] + offset = ( + state.sCount[ddLine] + + contentStart + - (state.bMarks[ddLine] + state.tShift[ddLine]) + ) + + while pos < maximum: + if state.src[pos] == "\t": + offset += 4 - offset % 4 + elif state.src[pos] == " ": + offset += 1 + else: + break + + pos += 1 + + contentStart = pos + + oldTight = state.tight + oldDDIndent = state.ddIndent + oldIndent = state.blkIndent + oldTShift = state.tShift[ddLine] + oldSCount = state.sCount[ddLine] + oldParentType = state.parentType + state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2 + state.tShift[ddLine] = contentStart - state.bMarks[ddLine] + state.sCount[ddLine] = offset + state.tight = True + state.parentType = "deflist" + + state.md.block.tokenize(state, ddLine, endLine) + + # If any of list item is tight, mark list as tight + if not state.tight or prevEmptyEnd: + tight = False + + # Item become loose if finish with empty line, + # but we should filter last element, because it means list finish + prevEmptyEnd = (state.line - ddLine) > 1 and state.isEmpty( + state.line - 1 + ) + + state.tShift[ddLine] = oldTShift + state.sCount[ddLine] = oldSCount + state.tight = oldTight + state.parentType = oldParentType + state.blkIndent = oldIndent + state.ddIndent = oldDDIndent + + token = state.push("dd_close", "dd", -1) + + itemLines[1] = nextLine = state.line + + if nextLine >= endLine: + break_outer = True + break + + if state.sCount[nextLine] < state.blkIndent: + break_outer = True + break + + contentStart = skipMarker(state, nextLine) + if contentStart < 0: + break + + ddLine = nextLine + + # go to the next loop iteration: + # insert DD tag and repeat checking + + if break_outer: + break_outer = False + break + + if nextLine >= endLine: + break + dtLine = nextLine + + if state.isEmpty(dtLine): + break + if state.sCount[dtLine] < state.blkIndent: + break + + ddLine = dtLine + 1 + if ddLine >= endLine: + break + if state.isEmpty(ddLine): + ddLine += 1 + if ddLine >= endLine: + break + + if state.sCount[ddLine] < state.blkIndent: + break + contentStart = skipMarker(state, ddLine) + if contentStart < 0: + break + + # go to the next loop iteration: + # insert DT and DD tags and repeat checking + + # Finalise list + token = state.push("dl_close", "dl", -1) + + listLines[1] = nextLine + + state.line = nextLine + + # mark paragraphs tight if needed + if tight: + markTightParagraphs(state, listTokIdx) + + return True + + md.block.ruler.before( + "paragraph", + "deflist", + deflist, + {"alt": ["paragraph", "reference", "blockquote"]}, + ) From 30875caadfefd18ed4b8c1d4735ca89091b6b443 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 20:55:38 -0600 Subject: [PATCH 3/8] fix(#56): do not format when deflist character is repeated --- mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py | 7 +++++++ pyproject.toml | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py index f5d20d1..40ddfca 100644 --- a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py @@ -1,5 +1,7 @@ """Process definition lists.""" +from contextlib import suppress + from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock @@ -42,6 +44,11 @@ def skipMarker(state: StateBlock, line: int) -> int: pos = state.skipSpaces(start) + # Skip if character is repeated + with suppress(IndexError): + if state.src[start] == state.src[start - 1]: + return -1 + # require space after ":" if start == pos: return -1 diff --git a/pyproject.toml b/pyproject.toml index 0259429..895597e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,6 +146,10 @@ known-first-party = ['mdformat_mkdocs', 'tests'] 'tests/format/test_wrap.py' = [ 'E501', # Line too long (122 > 88) ] +'mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py' = [ + 'FBT003', # Boolean positional value in function call + 'N806', # Variable `itemLines` in function should be lowercase +] [tool.ruff.lint.pydocstyle] convention = "google" From afcd1a99d766f9dfa9c329f332320969d7f739b6 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 20:55:41 -0600 Subject: [PATCH 4/8] Revert "fix(#56): do not format when deflist character is repeated" This reverts commit 30875caadfefd18ed4b8c1d4735ca89091b6b443. --- mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py | 7 ------- pyproject.toml | 4 ---- 2 files changed, 11 deletions(-) diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py index 40ddfca..f5d20d1 100644 --- a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py @@ -1,7 +1,5 @@ """Process definition lists.""" -from contextlib import suppress - from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock @@ -44,11 +42,6 @@ def skipMarker(state: StateBlock, line: int) -> int: pos = state.skipSpaces(start) - # Skip if character is repeated - with suppress(IndexError): - if state.src[start] == state.src[start - 1]: - return -1 - # require space after ":" if start == pos: return -1 diff --git a/pyproject.toml b/pyproject.toml index 895597e..0259429 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,10 +146,6 @@ known-first-party = ['mdformat_mkdocs', 'tests'] 'tests/format/test_wrap.py' = [ 'E501', # Line too long (122 > 88) ] -'mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py' = [ - 'FBT003', # Boolean positional value in function call - 'N806', # Variable `itemLines` in function should be lowercase -] [tool.ruff.lint.pydocstyle] convention = "google" From ec60d7e0db65aa85fe56c0212039a3c154177b36 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 20:55:43 -0600 Subject: [PATCH 5/8] Revert "feat: copy deflist plugin from mdit_py_plugins" This reverts commit 0a11de3137145e9941b2fd3de283e0c0d70234a1. --- .../mdit_plugins/_material_deflist.py | 2 +- .../mdit_plugins/_mdit_py_plugins/__init__.py | 0 .../mdit_plugins/_mdit_py_plugins/deflist.py | 253 ------------------ 3 files changed, 1 insertion(+), 254 deletions(-) delete mode 100644 mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py delete mode 100644 mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index 9231d76..e27e764 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -31,7 +31,7 @@ from typing import TYPE_CHECKING from markdown_it import MarkdownIt -from ._mdit_py_plugins.deflist import deflist_plugin +from mdit_py_plugins.deflist import deflist_plugin if TYPE_CHECKING: from markdown_it import MarkdownIt diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py b/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py deleted file mode 100644 index f5d20d1..0000000 --- a/mdformat_mkdocs/mdit_plugins/_mdit_py_plugins/deflist.py +++ /dev/null @@ -1,253 +0,0 @@ -"""Process definition lists.""" - -from markdown_it import MarkdownIt -from markdown_it.rules_block import StateBlock - -from mdit_py_plugins.utils import is_code_block - - -def deflist_plugin(md: MarkdownIt) -> None: - """Plugin ported from - `markdown-it-deflist `__. - - The syntax is based on - `pandoc definition lists `__: - - .. code-block:: md - - Term 1 - : Definition 1 long form - - second paragraph - - Term 2 with *inline markup* - ~ Definition 2a compact style - ~ Definition 2b - - """ - - def skipMarker(state: StateBlock, line: int) -> int: - """Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail.""" - start = state.bMarks[line] + state.tShift[line] - maximum = state.eMarks[line] - - if start >= maximum: - return -1 - - # Check bullet - marker = state.src[start] - start += 1 - if marker != "~" and marker != ":": - return -1 - - pos = state.skipSpaces(start) - - # require space after ":" - if start == pos: - return -1 - - # no empty definitions, e.g. " : " - if pos >= maximum: - return -1 - - return start - - def markTightParagraphs(state: StateBlock, idx: int) -> None: - level = state.level + 2 - - i = idx + 2 - l2 = len(state.tokens) - 2 - while i < l2: - if ( - state.tokens[i].level == level - and state.tokens[i].type == "paragraph_open" - ): - state.tokens[i + 2].hidden = True - state.tokens[i].hidden = True - i += 2 - i += 1 - - def deflist(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool: - if is_code_block(state, startLine): - return False - - if silent: - # quirk: validation mode validates a dd block only, not a whole deflist - if state.ddIndent < 0: - return False - return skipMarker(state, startLine) >= 0 - - nextLine = startLine + 1 - if nextLine >= endLine: - return False - - if state.isEmpty(nextLine): - nextLine += 1 - if nextLine >= endLine: - return False - - if state.sCount[nextLine] < state.blkIndent: - return False - contentStart = skipMarker(state, nextLine) - if contentStart < 0: - return False - - # Start list - listTokIdx = len(state.tokens) - tight = True - - token = state.push("dl_open", "dl", 1) - token.map = listLines = [startLine, 0] - - # Iterate list items - dtLine = startLine - ddLine = nextLine - - # One definition list can contain multiple DTs, - # and one DT can be followed by multiple DDs. - # - # Thus, there is two loops here, and label is - # needed to break out of the second one - # - break_outer = False - - while True: - prevEmptyEnd = False - - token = state.push("dt_open", "dt", 1) - token.map = [dtLine, dtLine] - - token = state.push("inline", "", 0) - token.map = [dtLine, dtLine] - token.content = state.getLines( - dtLine, dtLine + 1, state.blkIndent, False - ).strip() - token.children = [] - - token = state.push("dt_close", "dt", -1) - - while True: - token = state.push("dd_open", "dd", 1) - token.map = itemLines = [nextLine, 0] - - pos = contentStart - maximum = state.eMarks[ddLine] - offset = ( - state.sCount[ddLine] - + contentStart - - (state.bMarks[ddLine] + state.tShift[ddLine]) - ) - - while pos < maximum: - if state.src[pos] == "\t": - offset += 4 - offset % 4 - elif state.src[pos] == " ": - offset += 1 - else: - break - - pos += 1 - - contentStart = pos - - oldTight = state.tight - oldDDIndent = state.ddIndent - oldIndent = state.blkIndent - oldTShift = state.tShift[ddLine] - oldSCount = state.sCount[ddLine] - oldParentType = state.parentType - state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2 - state.tShift[ddLine] = contentStart - state.bMarks[ddLine] - state.sCount[ddLine] = offset - state.tight = True - state.parentType = "deflist" - - state.md.block.tokenize(state, ddLine, endLine) - - # If any of list item is tight, mark list as tight - if not state.tight or prevEmptyEnd: - tight = False - - # Item become loose if finish with empty line, - # but we should filter last element, because it means list finish - prevEmptyEnd = (state.line - ddLine) > 1 and state.isEmpty( - state.line - 1 - ) - - state.tShift[ddLine] = oldTShift - state.sCount[ddLine] = oldSCount - state.tight = oldTight - state.parentType = oldParentType - state.blkIndent = oldIndent - state.ddIndent = oldDDIndent - - token = state.push("dd_close", "dd", -1) - - itemLines[1] = nextLine = state.line - - if nextLine >= endLine: - break_outer = True - break - - if state.sCount[nextLine] < state.blkIndent: - break_outer = True - break - - contentStart = skipMarker(state, nextLine) - if contentStart < 0: - break - - ddLine = nextLine - - # go to the next loop iteration: - # insert DD tag and repeat checking - - if break_outer: - break_outer = False - break - - if nextLine >= endLine: - break - dtLine = nextLine - - if state.isEmpty(dtLine): - break - if state.sCount[dtLine] < state.blkIndent: - break - - ddLine = dtLine + 1 - if ddLine >= endLine: - break - if state.isEmpty(ddLine): - ddLine += 1 - if ddLine >= endLine: - break - - if state.sCount[ddLine] < state.blkIndent: - break - contentStart = skipMarker(state, ddLine) - if contentStart < 0: - break - - # go to the next loop iteration: - # insert DT and DD tags and repeat checking - - # Finalise list - token = state.push("dl_close", "dl", -1) - - listLines[1] = nextLine - - state.line = nextLine - - # mark paragraphs tight if needed - if tight: - markTightParagraphs(state, listTokIdx) - - return True - - md.block.ruler.before( - "paragraph", - "deflist", - deflist, - {"alt": ["paragraph", "reference", "blockquote"]}, - ) From 05dfd54e52f37a91466030e918d69323f8bca8dc Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 21:02:54 -0600 Subject: [PATCH 6/8] fix(#56): prevent escaping deflist --- mdformat_mkdocs/mdit_plugins/__init__.py | 2 -- mdformat_mkdocs/mdit_plugins/_material_deflist.py | 11 ----------- mdformat_mkdocs/plugin.py | 2 -- tests/format/fixtures/material_deflist.md | 4 ++-- 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/mdformat_mkdocs/mdit_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/__init__.py index ef33809..7f696f7 100644 --- a/mdformat_mkdocs/mdit_plugins/__init__.py +++ b/mdformat_mkdocs/mdit_plugins/__init__.py @@ -6,7 +6,6 @@ material_content_tabs_plugin, ) from ._material_deflist import ( - escape_deflist, material_deflist_plugin, render_material_definition_body, render_material_definition_list, @@ -40,7 +39,6 @@ "PYMD_CAPTIONS_PREFIX", "PYMD_SNIPPET_PREFIX", "PYTHON_MARKDOWN_ATTR_LIST_PREFIX", - "escape_deflist", "material_admon_plugin", "material_content_tabs_plugin", "material_deflist_plugin", diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index e27e764..2f69059 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -98,14 +98,3 @@ def render_material_definition_body( ) finally: context.env["indent_width"] -= indent_width - - -def escape_deflist( - text: str, - node: RenderTreeNode, # noqa: ARG001 - context: RenderContext, # noqa: ARG001 -) -> str: - """Escape line starting ":" which would otherwise be parsed as a definition list.""" - return "\n".join( - "\\" + line if line.startswith(":") else line for line in text.split("\n") - ) diff --git a/mdformat_mkdocs/plugin.py b/mdformat_mkdocs/plugin.py index ec4ccb2..77f85f9 100644 --- a/mdformat_mkdocs/plugin.py +++ b/mdformat_mkdocs/plugin.py @@ -19,7 +19,6 @@ PYMD_CAPTIONS_PREFIX, PYMD_SNIPPET_PREFIX, PYTHON_MARKDOWN_ATTR_LIST_PREFIX, - escape_deflist, material_admon_plugin, material_content_tabs_plugin, material_deflist_plugin, @@ -238,5 +237,4 @@ def render_pymd_caption(node: RenderTreeNode, context: RenderContext) -> str: "bullet_list": normalize_list, "inline": postprocess_list_wrap, # type: ignore[has-type] "ordered_list": normalize_list, - "paragraph": escape_deflist, } diff --git a/tests/format/fixtures/material_deflist.md b/tests/format/fixtures/material_deflist.md index 9098e10..cc3a193 100644 --- a/tests/format/fixtures/material_deflist.md +++ b/tests/format/fixtures/material_deflist.md @@ -169,11 +169,11 @@ foo : baz . -Escaped deflist +Escaped deflist (supported by the deflist plugin, but not mdformat-mkdocs for now - see https://github.com/KyleKing/mdformat-mkdocs/issues/56) . Term 1 \: Definition . Term 1 -\: Definition +: Definition . From e836ff56076890419a0c4026e6f31c467d4d580e Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 21:13:11 -0600 Subject: [PATCH 7/8] fix(#56): restore escape_deflist --- mdformat_mkdocs/mdit_plugins/__init__.py | 2 ++ mdformat_mkdocs/mdit_plugins/_material_deflist.py | 13 +++++++++++++ mdformat_mkdocs/plugin.py | 2 ++ tests/format/fixtures/material_deflist.md | 4 ++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/mdformat_mkdocs/mdit_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/__init__.py index 7f696f7..ef33809 100644 --- a/mdformat_mkdocs/mdit_plugins/__init__.py +++ b/mdformat_mkdocs/mdit_plugins/__init__.py @@ -6,6 +6,7 @@ material_content_tabs_plugin, ) from ._material_deflist import ( + escape_deflist, material_deflist_plugin, render_material_definition_body, render_material_definition_list, @@ -39,6 +40,7 @@ "PYMD_CAPTIONS_PREFIX", "PYMD_SNIPPET_PREFIX", "PYTHON_MARKDOWN_ATTR_LIST_PREFIX", + "escape_deflist", "material_admon_plugin", "material_content_tabs_plugin", "material_deflist_plugin", diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index 2f69059..44fefdc 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -28,6 +28,7 @@ from __future__ import annotations +import re from typing import TYPE_CHECKING from markdown_it import MarkdownIt @@ -98,3 +99,15 @@ def render_material_definition_body( ) finally: context.env["indent_width"] -= indent_width + + +def escape_deflist( + text: str, + node: RenderTreeNode, # noqa: ARG001 + context: RenderContext, # noqa: ARG001 +) -> str: + """Escape line starting ":" which would otherwise be parsed as a definition list.""" + pattern = re.compile(r"^[:~] ") + return "\n".join( + "\\" + line if pattern.match(line) else line for line in text.split("\n") + ) diff --git a/mdformat_mkdocs/plugin.py b/mdformat_mkdocs/plugin.py index 77f85f9..ec4ccb2 100644 --- a/mdformat_mkdocs/plugin.py +++ b/mdformat_mkdocs/plugin.py @@ -19,6 +19,7 @@ PYMD_CAPTIONS_PREFIX, PYMD_SNIPPET_PREFIX, PYTHON_MARKDOWN_ATTR_LIST_PREFIX, + escape_deflist, material_admon_plugin, material_content_tabs_plugin, material_deflist_plugin, @@ -237,4 +238,5 @@ def render_pymd_caption(node: RenderTreeNode, context: RenderContext) -> str: "bullet_list": normalize_list, "inline": postprocess_list_wrap, # type: ignore[has-type] "ordered_list": normalize_list, + "paragraph": escape_deflist, } diff --git a/tests/format/fixtures/material_deflist.md b/tests/format/fixtures/material_deflist.md index cc3a193..9098e10 100644 --- a/tests/format/fixtures/material_deflist.md +++ b/tests/format/fixtures/material_deflist.md @@ -169,11 +169,11 @@ foo : baz . -Escaped deflist (supported by the deflist plugin, but not mdformat-mkdocs for now - see https://github.com/KyleKing/mdformat-mkdocs/issues/56) +Escaped deflist . Term 1 \: Definition . Term 1 -: Definition +\: Definition . From 5eaa46336e2a6cb8a7e3bdad1aaac5c3c5324d53 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Tue, 19 Aug 2025 21:13:30 -0600 Subject: [PATCH 8/8] refactor: prevent typo correction on Nam --- mdformat_mkdocs/mdit_plugins/_material_deflist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index 44fefdc..e29179c 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -17,7 +17,7 @@ ut eros sed sapien ullamcorper consequat. Nunc ligula ante. Duis mollis est eget nibh volutpat, fermentum aliquet dui mollis. - Nam vulputate tincidunt fringilla. + Vulputate tincidunt fringilla. Nullam dignissim ultrices urna non auctor. ```