From e2be0deea8e37bfce4a0755001eb2cebea0178c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 09:43:46 +0200 Subject: [PATCH 1/6] fix: preserve indentation and parse options with blank lines in nested mkdocstrings injection blocks --- mdformat_mkdocs/__init__.py | 1 - mdformat_mkdocs/_normalize_list.py | 59 +++++++++++-- mdformat_mkdocs/mdit_plugins/__init__.py | 2 + .../mdit_plugins/_material_deflist.py | 1 - .../mdit_plugins/_mkdocstrings_injection.py | 12 ++- .../_python_markdown_attr_list.py | 1 - mise.lock | 86 +++++++++++++++++++ .../format/fixtures/mkdocstrings_injection.md | 84 ++++++++++++++++++ tests/format/test_format.py | 1 + 9 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 tests/format/fixtures/mkdocstrings_injection.md diff --git a/mdformat_mkdocs/__init__.py b/mdformat_mkdocs/__init__.py index 1d03487..54332ad 100644 --- a/mdformat_mkdocs/__init__.py +++ b/mdformat_mkdocs/__init__.py @@ -1,4 +1,3 @@ -# ruff: noqa: RUF067 """An mdformat plugin for `mkdocs`.""" __version__ = "5.2.0b2" diff --git a/mdformat_mkdocs/_normalize_list.py b/mdformat_mkdocs/_normalize_list.py index 2f80a49..8d546b0 100644 --- a/mdformat_mkdocs/_normalize_list.py +++ b/mdformat_mkdocs/_normalize_list.py @@ -19,7 +19,11 @@ rstrip_result, separate_indent, ) -from .mdit_plugins import MATERIAL_ADMON_MARKERS, MATERIAL_CONTENT_TAB_MARKERS +from .mdit_plugins import ( + INJECTION_PATTERN, + MATERIAL_ADMON_MARKERS, + MATERIAL_CONTENT_TAB_MARKERS, +) if TYPE_CHECKING: from collections.abc import Mapping @@ -71,6 +75,7 @@ class Syntax(Enum): START_MARKED = "START_MARKED" EDGE_CODE = "EDGE_CODE" HTML = "HTML" + INJECTION = "INJECTION" @classmethod def from_content(cls, content: str) -> Syntax | None: @@ -85,13 +90,17 @@ def from_content(cls, content: str) -> Syntax | None: if match["item"].startswith("```"): return cls.CODE_NUMBERED if is_numbered else cls.CODE_BULLETED return cls.LIST_NUMBERED if is_numbered else cls.LIST_BULLETED + + result = None if any(content.startswith(f"{marker} ") for marker in MARKERS): - return cls.START_MARKED - if content.startswith("```"): - return cls.EDGE_CODE - if content.startswith("<"): - return cls.HTML - return None + result = cls.START_MARKED + elif content.startswith("```"): + result = cls.EDGE_CODE + elif content.startswith("<"): + result = cls.HTML + elif INJECTION_PATTERN.match(content): + result = cls.INJECTION + return result SYNTAX_CODE_LIST = {Syntax.CODE_BULLETED, Syntax.CODE_NUMBERED} @@ -254,6 +263,32 @@ def _parse_html_line(last: BlockIndent | None, line: LineResult) -> BlockIndent return result +def _parse_injection_block( + last: BlockIndent | None, + line: LineResult, +) -> BlockIndent | None: + """Identify mkdocstrings injection sections.""" + if last is not None: + if line.parsed.content and len(line.parsed.indent) <= len(last.raw_indent): + if line.parsed.syntax == Syntax.INJECTION: + return BlockIndent( + start_line=line.parsed.line_num, + raw_indent=line.parsed.indent, + indent_depth=len(line.parents), + kind="code", + ) + return None + return last + if line.parsed.syntax == Syntax.INJECTION: + return BlockIndent( + start_line=line.parsed.line_num, + raw_indent=line.parsed.indent, + indent_depth=len(line.parents), + kind="code", + ) + return None + + # ====================================================================================== # Semantic Indent Handling @@ -420,9 +455,15 @@ def parse_text( indent if (indent and code_indents[indent.start_line] is None) else None for indent in map_lookback(_parse_html_line, lines, None) ] - # When both, code_indents take precedence + injection_indents = [ + # Any indents initiated from within a `code_block_indents` should be ignored + indent if (indent and code_indents[indent.start_line] is None) else None + for indent in map_lookback(_parse_injection_block, lines, None) + ] + # When multiple match, code_indents take precedence, then html_indents block_indents = [ - c_ or h_ for c_, h_ in zip(code_indents, html_indents, strict=True) + c_ or h_ or i_ + for c_, h_, i_ in zip(code_indents, html_indents, injection_indents, strict=True) ] new_indents = [*starmap(_format_new_indent, zip(lines, block_indents, strict=True))] diff --git a/mdformat_mkdocs/mdit_plugins/__init__.py b/mdformat_mkdocs/mdit_plugins/__init__.py index 77e4f09..0fc13d2 100644 --- a/mdformat_mkdocs/mdit_plugins/__init__.py +++ b/mdformat_mkdocs/mdit_plugins/__init__.py @@ -22,6 +22,7 @@ mkdocstrings_crossreference_plugin, ) from ._mkdocstrings_injection import ( + INJECTION_PATTERN, MKDOCSTRINGS_INJECTION_PREFIX, mkdocstrings_injection_plugin, ) @@ -48,6 +49,7 @@ "DOLLARMATH_BLOCK", "DOLLARMATH_BLOCK_LABEL", "DOLLARMATH_INLINE", + "INJECTION_PATTERN", "MATERIAL_ADMON_MARKERS", "MATERIAL_CONTENT_TAB_MARKERS", "MKDOCSTRINGS_AUTOREFS_PREFIX", diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index e29179c..8214188 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -35,7 +35,6 @@ from mdit_py_plugins.deflist import deflist_plugin if TYPE_CHECKING: - from markdown_it import MarkdownIt from mdformat.renderer import RenderContext, RenderTreeNode from mdformat.renderer.typing import Render diff --git a/mdformat_mkdocs/mdit_plugins/_mkdocstrings_injection.py b/mdformat_mkdocs/mdit_plugins/_mkdocstrings_injection.py index 17fcf59..a4c5adc 100644 --- a/mdformat_mkdocs/mdit_plugins/_mkdocstrings_injection.py +++ b/mdformat_mkdocs/mdit_plugins/_mkdocstrings_injection.py @@ -23,7 +23,7 @@ from markdown_it import MarkdownIt from markdown_it.rules_block import StateBlock -_INJECTION_PATTERN = re.compile(r"^:::\s+\S") +INJECTION_PATTERN = re.compile(r"^:::\s+\S") MKDOCSTRINGS_INJECTION_PREFIX = "mkdocstrings_injection" @@ -40,9 +40,9 @@ def _mkdocstrings_injection( if is_code_block(state, start_line): return False - base_indent = state.blkIndent + base_indent = state.tShift[start_line] header = _get_line(state, start_line, base_indent) - if not _INJECTION_PATTERN.match(header): + if not INJECTION_PATTERN.match(header): return False if silent: @@ -51,11 +51,15 @@ def _mkdocstrings_injection( lines = [header] next_line = start_line + 1 while next_line < end_line: - if state.tShift[next_line] <= base_indent: + if not state.isEmpty(next_line) and state.tShift[next_line] <= base_indent: break lines.append(_get_line(state, next_line, base_indent)) next_line += 1 + while len(lines) > 1 and not lines[-1].strip(): + lines.pop() + next_line -= 1 + token = state.push(MKDOCSTRINGS_INJECTION_PREFIX, "", 0) token.content = "\n".join(lines) token.block = True diff --git a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py index 4236899..4a188bd 100644 --- a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py +++ b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py @@ -17,7 +17,6 @@ from mdformat_mkdocs._synced.admon_factories import new_token if TYPE_CHECKING: - from markdown_it import MarkdownIt from markdown_it.rules_inline import StateInline _ATTR_LIST_PATTERN = re.compile(r"{:? (?P[^}]+) }") diff --git a/mise.lock b/mise.lock index 5253063..3f73a7d 100644 --- a/mise.lock +++ b/mise.lock @@ -1,3 +1,5 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html + [[tools."cargo:mdsf"]] version = "0.11.0" backend = "cargo:mdsf" @@ -21,10 +23,94 @@ uvx_args = "--with tox-uv==1.29.0" version = "3.10.15" backend = "core:python" +[tools.python."platforms.linux-arm64"] +checksum = "sha256:6008b42df79a0c8a4efe3aa88c2aea1471116aa66881a8ed15f04d66438cb7f5" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-arm64-musl"] +checksum = "sha256:6008b42df79a0c8a4efe3aa88c2aea1471116aa66881a8ed15f04d66438cb7f5" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-x64"] +checksum = "blake3:11b68f55ac786adb58361bda858a80b5a01e185578a815ce448b3af5ba9de983" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-x64-musl"] +checksum = "sha256:25fb8e23cd3b82b748075a04fd18f3183cc7316c11d6f59eb4b0326843892600" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.macos-arm64"] +checksum = "sha256:fa79bd909bfeb627ffe66a8b023153495ece659e5e3b2ff56268535024db851c" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-apple-darwin-install_only_stripped.tar.gz" + +[tools.python."platforms.macos-x64"] +checksum = "sha256:0d952fa2342794523ea7beee6a58e79e62045d0f018314ce282e9f2f1427ee2c" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-apple-darwin-install_only_stripped.tar.gz" + +[tools.python."platforms.windows-x64"] +checksum = "sha256:45a95225c659f9b988f444d985df347140ecc71c0297c6857febf5ef440d689a" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" + [[tools.python]] version = "3.14.0" backend = "core:python" +[tools.python."platforms.linux-arm64"] +checksum = "sha256:7603b1905e5d280191b2146599474fe35da0833c81a09512c28b9b77e651812d" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-arm64-musl"] +checksum = "sha256:7603b1905e5d280191b2146599474fe35da0833c81a09512c28b9b77e651812d" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-x64"] +checksum = "blake3:53d6d31892bc79df588305e7d803c52cd7996dec24f8a923aeb1cf8ad5a98a5c" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.linux-x64-musl"] +checksum = "sha256:66f385305ae0eefd6b65e2cf942bc91d943a61e26fb7581e029f760a2b04f393" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" + +[tools.python."platforms.macos-arm64"] +checksum = "sha256:5995f024e9c95ccbc87b9bb97152ad1ec9efcf9d76e0574cd37dd946afbea3c6" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-apple-darwin-install_only_stripped.tar.gz" + +[tools.python."platforms.macos-x64"] +checksum = "sha256:eb0a5915f58c3dcb64d587e951f8942e6c9c6af63fab8bbb5596d1ce5738c2a4" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-apple-darwin-install_only_stripped.tar.gz" + +[tools.python."platforms.windows-x64"] +checksum = "sha256:42a45020cff6a33e0d4f38f63d68c81240f0efa765e1e5a23693fb4c4a9f5e9a" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" + [[tools.uv]] version = "0.9.11" backend = "aqua:astral-sh/uv" + +[tools.uv."platforms.linux-arm64"] +checksum = "sha256:26b33bb65714247dd865216e71c5528030c85c6de9ce06172152158ed7add70c" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-unknown-linux-musl.tar.gz" + +[tools.uv."platforms.linux-arm64-musl"] +checksum = "sha256:26b33bb65714247dd865216e71c5528030c85c6de9ce06172152158ed7add70c" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-unknown-linux-musl.tar.gz" + +[tools.uv."platforms.linux-x64"] +checksum = "sha256:5cc06fe71374a8883aeb2c83a141a4b5fac8584ee894ba31c5792254508b4e9a" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-unknown-linux-musl.tar.gz" + +[tools.uv."platforms.linux-x64-musl"] +checksum = "sha256:5cc06fe71374a8883aeb2c83a141a4b5fac8584ee894ba31c5792254508b4e9a" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-unknown-linux-musl.tar.gz" + +[tools.uv."platforms.macos-arm64"] +checksum = "sha256:594d9f4cfbd21d5a2f34b0352bf423066a9dab1733c90b5d40e3e227506deb03" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-apple-darwin.tar.gz" + +[tools.uv."platforms.macos-x64"] +checksum = "sha256:14236594b4edbd90929d845766a41a1d4e51d530c9ebbedfb3d93688661f142c" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-apple-darwin.tar.gz" + +[tools.uv."platforms.windows-x64"] +checksum = "sha256:45a3ff2a68c246ed9fd2d9df032496c1beebe480357f356ac25d2cb144884c30" +url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-pc-windows-msvc.zip" diff --git a/tests/format/fixtures/mkdocstrings_injection.md b/tests/format/fixtures/mkdocstrings_injection.md new file mode 100644 index 0000000..67998a7 --- /dev/null +++ b/tests/format/fixtures/mkdocstrings_injection.md @@ -0,0 +1,84 @@ +Standard mkdocstrings injection +. +::: package.module.Class +. +::: package.module.Class +. + +mkdocstrings injection with options +. +::: package.module.Class + options: + heading_level: 2 + show_source: true +. +::: package.module.Class + options: + heading_level: 2 + show_source: true +. + +mkdocstrings injection with blank line in options +. +::: package.module.Class + options: + heading_level: 2 + + show_source: true +. +::: package.module.Class + options: + heading_level: 2 + + show_source: true +. + +mkdocstrings injection nested in list +. +- List item: + ::: package.module.Class + options: + heading_level: 2 +. +- List item: + ::: package.module.Class + options: + heading_level: 2 +. + +mkdocstrings injection nested in list with blank line +. +- List item: + ::: package.module.Class + options: + heading_level: 2 + + show_source: true +. +- List item: + ::: package.module.Class + options: + heading_level: 2 + + show_source: true +. + +mkdocstrings injection with private submodule +. +::: my_package._private_module +. +::: my_package._private_module +. + +mkdocstrings injection with private submodule nested in list +. +- List item: + ::: my_package._private_module + options: + heading_level: 2 +. +- List item: + ::: my_package._private_module + options: + heading_level: 2 +. diff --git a/tests/format/test_format.py b/tests/format/test_format.py index 9f52dc5..99cba44 100644 --- a/tests/format/test_format.py +++ b/tests/format/test_format.py @@ -41,6 +41,7 @@ def flatten(nested_list: list[list[T]]) -> list[T]: "material_math.md", "math_with_mkdocs_features.md", "mkdocstrings_autorefs.md", + "mkdocstrings_injection.md", "pymd_abbreviations.md", "pymd_arithmatex.md", "pymd_arithmatex_ams_environments.md", From f98c785d4669b86b15a416094d7fce6fd9dd132f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 09:59:34 +0200 Subject: [PATCH 2/6] revert mise.lock changes --- mise.lock | 86 ------------------------------------------------------- 1 file changed, 86 deletions(-) diff --git a/mise.lock b/mise.lock index 3f73a7d..5253063 100644 --- a/mise.lock +++ b/mise.lock @@ -1,5 +1,3 @@ -# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html - [[tools."cargo:mdsf"]] version = "0.11.0" backend = "cargo:mdsf" @@ -23,94 +21,10 @@ uvx_args = "--with tox-uv==1.29.0" version = "3.10.15" backend = "core:python" -[tools.python."platforms.linux-arm64"] -checksum = "sha256:6008b42df79a0c8a4efe3aa88c2aea1471116aa66881a8ed15f04d66438cb7f5" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-arm64-musl"] -checksum = "sha256:6008b42df79a0c8a4efe3aa88c2aea1471116aa66881a8ed15f04d66438cb7f5" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-x64"] -checksum = "blake3:11b68f55ac786adb58361bda858a80b5a01e185578a815ce448b3af5ba9de983" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-x64-musl"] -checksum = "sha256:25fb8e23cd3b82b748075a04fd18f3183cc7316c11d6f59eb4b0326843892600" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.macos-arm64"] -checksum = "sha256:fa79bd909bfeb627ffe66a8b023153495ece659e5e3b2ff56268535024db851c" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-aarch64-apple-darwin-install_only_stripped.tar.gz" - -[tools.python."platforms.macos-x64"] -checksum = "sha256:0d952fa2342794523ea7beee6a58e79e62045d0f018314ce282e9f2f1427ee2c" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-apple-darwin-install_only_stripped.tar.gz" - -[tools.python."platforms.windows-x64"] -checksum = "sha256:45a95225c659f9b988f444d985df347140ecc71c0297c6857febf5ef440d689a" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20241016/cpython-3.10.15+20241016-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" - [[tools.python]] version = "3.14.0" backend = "core:python" -[tools.python."platforms.linux-arm64"] -checksum = "sha256:7603b1905e5d280191b2146599474fe35da0833c81a09512c28b9b77e651812d" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-arm64-musl"] -checksum = "sha256:7603b1905e5d280191b2146599474fe35da0833c81a09512c28b9b77e651812d" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-x64"] -checksum = "blake3:53d6d31892bc79df588305e7d803c52cd7996dec24f8a923aeb1cf8ad5a98a5c" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.linux-x64-musl"] -checksum = "sha256:66f385305ae0eefd6b65e2cf942bc91d943a61e26fb7581e029f760a2b04f393" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" - -[tools.python."platforms.macos-arm64"] -checksum = "sha256:5995f024e9c95ccbc87b9bb97152ad1ec9efcf9d76e0574cd37dd946afbea3c6" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-aarch64-apple-darwin-install_only_stripped.tar.gz" - -[tools.python."platforms.macos-x64"] -checksum = "sha256:eb0a5915f58c3dcb64d587e951f8942e6c9c6af63fab8bbb5596d1ce5738c2a4" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-apple-darwin-install_only_stripped.tar.gz" - -[tools.python."platforms.windows-x64"] -checksum = "sha256:42a45020cff6a33e0d4f38f63d68c81240f0efa765e1e5a23693fb4c4a9f5e9a" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.14.0+20251120-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" - [[tools.uv]] version = "0.9.11" backend = "aqua:astral-sh/uv" - -[tools.uv."platforms.linux-arm64"] -checksum = "sha256:26b33bb65714247dd865216e71c5528030c85c6de9ce06172152158ed7add70c" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-unknown-linux-musl.tar.gz" - -[tools.uv."platforms.linux-arm64-musl"] -checksum = "sha256:26b33bb65714247dd865216e71c5528030c85c6de9ce06172152158ed7add70c" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-unknown-linux-musl.tar.gz" - -[tools.uv."platforms.linux-x64"] -checksum = "sha256:5cc06fe71374a8883aeb2c83a141a4b5fac8584ee894ba31c5792254508b4e9a" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-unknown-linux-musl.tar.gz" - -[tools.uv."platforms.linux-x64-musl"] -checksum = "sha256:5cc06fe71374a8883aeb2c83a141a4b5fac8584ee894ba31c5792254508b4e9a" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-unknown-linux-musl.tar.gz" - -[tools.uv."platforms.macos-arm64"] -checksum = "sha256:594d9f4cfbd21d5a2f34b0352bf423066a9dab1733c90b5d40e3e227506deb03" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-aarch64-apple-darwin.tar.gz" - -[tools.uv."platforms.macos-x64"] -checksum = "sha256:14236594b4edbd90929d845766a41a1d4e51d530c9ebbedfb3d93688661f142c" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-apple-darwin.tar.gz" - -[tools.uv."platforms.windows-x64"] -checksum = "sha256:45a3ff2a68c246ed9fd2d9df032496c1beebe480357f356ac25d2cb144884c30" -url = "https://github.com/astral-sh/uv/releases/download/0.9.11/uv-x86_64-pc-windows-msvc.zip" From 2fb06c9477e21a3664df1640082c52eaf29d1663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 10:02:05 +0200 Subject: [PATCH 3/6] revert init file --- mdformat_mkdocs/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mdformat_mkdocs/__init__.py b/mdformat_mkdocs/__init__.py index 54332ad..1d03487 100644 --- a/mdformat_mkdocs/__init__.py +++ b/mdformat_mkdocs/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: RUF067 """An mdformat plugin for `mkdocs`.""" __version__ = "5.2.0b2" From af2210f67be43878a5c9a5bcdd4c4030269f7805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 10:05:34 +0200 Subject: [PATCH 4/6] revert changes in deflist --- mdformat_mkdocs/mdit_plugins/_material_deflist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index 8214188..e29179c 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -35,6 +35,7 @@ from mdit_py_plugins.deflist import deflist_plugin if TYPE_CHECKING: + from markdown_it import MarkdownIt from mdformat.renderer import RenderContext, RenderTreeNode from mdformat.renderer.typing import Render From 659def87216ddcaa8d83fe51265c257a610181a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 10:09:11 +0200 Subject: [PATCH 5/6] revert changes in attrlist --- mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py index 4a188bd..4236899 100644 --- a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py +++ b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py @@ -17,6 +17,7 @@ from mdformat_mkdocs._synced.admon_factories import new_token if TYPE_CHECKING: + from markdown_it import MarkdownIt from markdown_it.rules_inline import StateInline _ATTR_LIST_PATTERN = re.compile(r"{:? (?P[^}]+) }") From 6cfa449e66ec28246f99ee0e8ce2742a60d41ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Marcin=20Wr=C3=B3blewski?= Date: Sat, 27 Jun 2026 20:45:35 +0200 Subject: [PATCH 6/6] fix: preserve list-like YAML options inside mkdocstrings injection blocks and add snapshot tests --- mdformat_mkdocs/__init__.py | 1 - mdformat_mkdocs/_normalize_list.py | 8 +- .../mdit_plugins/_material_deflist.py | 1 - .../_python_markdown_attr_list.py | 1 - .../__snapshots__/test_parsed_result.ambr | 791 ++++++++++++++++++ tests/format/test_parsed_result.py | 12 +- 6 files changed, 807 insertions(+), 7 deletions(-) diff --git a/mdformat_mkdocs/__init__.py b/mdformat_mkdocs/__init__.py index 1d03487..54332ad 100644 --- a/mdformat_mkdocs/__init__.py +++ b/mdformat_mkdocs/__init__.py @@ -1,4 +1,3 @@ -# ruff: noqa: RUF067 """An mdformat plugin for `mkdocs`.""" __version__ = "5.2.0b2" diff --git a/mdformat_mkdocs/_normalize_list.py b/mdformat_mkdocs/_normalize_list.py index 8d546b0..a4a5267 100644 --- a/mdformat_mkdocs/_normalize_list.py +++ b/mdformat_mkdocs/_normalize_list.py @@ -468,8 +468,12 @@ def parse_text( new_indents = [*starmap(_format_new_indent, zip(lines, block_indents, strict=True))] new_contents = [ - _format_new_content(line, inc_numbers, ci is not None) - for line, ci in zip(lines, code_indents, strict=True) + _format_new_content( + line, + inc_numbers, + block_indent is not None and block_indent.kind == "code", + ) + for line, block_indent in zip(lines, block_indents, strict=True) ] if use_sem_break: diff --git a/mdformat_mkdocs/mdit_plugins/_material_deflist.py b/mdformat_mkdocs/mdit_plugins/_material_deflist.py index e29179c..8214188 100644 --- a/mdformat_mkdocs/mdit_plugins/_material_deflist.py +++ b/mdformat_mkdocs/mdit_plugins/_material_deflist.py @@ -35,7 +35,6 @@ from mdit_py_plugins.deflist import deflist_plugin if TYPE_CHECKING: - from markdown_it import MarkdownIt from mdformat.renderer import RenderContext, RenderTreeNode from mdformat.renderer.typing import Render diff --git a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py index 4236899..4a188bd 100644 --- a/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py +++ b/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py @@ -17,7 +17,6 @@ from mdformat_mkdocs._synced.admon_factories import new_token if TYPE_CHECKING: - from markdown_it import MarkdownIt from markdown_it.rules_inline import StateInline _ATTR_LIST_PATTERN = re.compile(r"{:? (?P[^}]+) }") diff --git a/tests/format/__snapshots__/test_parsed_result.ambr b/tests/format/__snapshots__/test_parsed_result.ambr index eca47b2..cd00d40 100644 --- a/tests/format/__snapshots__/test_parsed_result.ambr +++ b/tests/format/__snapshots__/test_parsed_result.ambr @@ -1699,6 +1699,38 @@ ]), ) # --- +# name: test_parsed_result[Standard mkdocstrings injection] + ParsedText( + debug_block_indents=list([ + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '::: package.module.Class', + ), + ]), + ) +# --- # name: test_parsed_result[Support inline bulleted code (https://github.com/KyleKing/mdformat-mkdocs/issues/40)] ParsedText( debug_block_indents=list([ @@ -2420,3 +2452,762 @@ ]), ) # --- +# name: test_parsed_result[mkdocstrings injection nested in list with blank line] + ParsedText( + debug_block_indents=list([ + None, + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ]), + parsed=ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + ]), + parsed=ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='heading_level: 2', + indent=' ', + line_num=3, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='', + indent='', + line_num=4, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='show_source: true', + indent=' ', + line_num=5, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '- List item:', + ), + tuple( + ' ', + '::: package.module.Class', + ), + tuple( + ' ', + 'options:', + ), + tuple( + ' ', + 'heading_level: 2', + ), + tuple( + '', + '', + ), + tuple( + ' ', + 'show_source: true', + ), + ]), + ) +# --- +# name: test_parsed_result[mkdocstrings injection nested in list] + ParsedText( + debug_block_indents=list([ + None, + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ]), + parsed=ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + ]), + parsed=ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: package.module.Class', + indent=' ', + line_num=1, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='heading_level: 2', + indent=' ', + line_num=3, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '- List item:', + ), + tuple( + ' ', + '::: package.module.Class', + ), + tuple( + ' ', + 'options:', + ), + tuple( + ' ', + 'heading_level: 2', + ), + ]), + ) +# --- +# name: test_parsed_result[mkdocstrings injection with blank line in options] + ParsedText( + debug_block_indents=list([ + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ]), + parsed=ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='heading_level: 2', + indent=' ', + line_num=2, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='', + indent='', + line_num=3, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='show_source: true', + indent=' ', + line_num=4, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '::: package.module.Class', + ), + tuple( + ' ', + 'options:', + ), + tuple( + ' ', + 'heading_level: 2', + ), + tuple( + '', + '', + ), + tuple( + ' ', + 'show_source: true', + ), + ]), + ) +# --- +# name: test_parsed_result[mkdocstrings injection with options] + ParsedText( + debug_block_indents=list([ + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ]), + parsed=ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='heading_level: 2', + indent=' ', + line_num=2, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='::: package.module.Class', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=1, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='show_source: true', + indent=' ', + line_num=3, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '::: package.module.Class', + ), + tuple( + ' ', + 'options:', + ), + tuple( + ' ', + 'heading_level: 2', + ), + tuple( + ' ', + 'show_source: true', + ), + ]), + ) +# --- +# name: test_parsed_result[mkdocstrings injection with private submodule nested in list] + ParsedText( + debug_block_indents=list([ + None, + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + BlockIndent( + indent_depth=1, + kind='code', + raw_indent=' ', + start_line=1, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ]), + parsed=ParsedLine( + content='::: my_package._private_module', + indent=' ', + line_num=1, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: my_package._private_module', + indent=' ', + line_num=1, + syntax=, + ), + ]), + parsed=ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + LineResult( + parents=list([ + ParsedLine( + content='- List item:', + indent='', + line_num=0, + syntax=, + ), + ParsedLine( + content='::: my_package._private_module', + indent=' ', + line_num=1, + syntax=, + ), + ParsedLine( + content='options:', + indent=' ', + line_num=2, + syntax=None, + ), + ]), + parsed=ParsedLine( + content='heading_level: 2', + indent=' ', + line_num=3, + syntax=None, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '- List item:', + ), + tuple( + ' ', + '::: my_package._private_module', + ), + tuple( + ' ', + 'options:', + ), + tuple( + ' ', + 'heading_level: 2', + ), + ]), + ) +# --- +# name: test_parsed_result[mkdocstrings injection with private submodule] + ParsedText( + debug_block_indents=list([ + BlockIndent( + indent_depth=0, + kind='code', + raw_indent='', + start_line=0, + ), + ]), + debug_original_lines=list([ + LineResult( + parents=list([ + ]), + parsed=ParsedLine( + content='::: my_package._private_module', + indent='', + line_num=0, + syntax=, + ), + prev_list_peers=list([ + ]), + ), + ]), + new_lines=list([ + tuple( + '', + '::: my_package._private_module', + ), + ]), + ) +# --- diff --git a/tests/format/test_parsed_result.py b/tests/format/test_parsed_result.py index 6a2b28d..f974e21 100644 --- a/tests/format/test_parsed_result.py +++ b/tests/format/test_parsed_result.py @@ -1,3 +1,4 @@ +from itertools import chain from pathlib import Path import pytest @@ -5,8 +6,15 @@ from mdformat_mkdocs._normalize_list import parse_text -FIXTURE_PATH = Path(__file__).parent / "fixtures/parsed_result.md" -fixtures = read_fixture_file(FIXTURE_PATH) +FIXTURE_PATHS = [ + Path(__file__).parent / "fixtures/parsed_result.md", + Path(__file__).parent / "fixtures/mkdocstrings_injection.md", +] +fixtures = list( + chain.from_iterable( + read_fixture_file(path) for path in FIXTURE_PATHS + ) +) @pytest.mark.parametrize(