Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mdformat_mkdocs/mdit_plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
MATERIAL_CONTENT_TAB_MARKERS,
material_content_tabs_plugin,
)
from ._material_deflist import (
escape_deflist,
material_deflist_plugin,
render_material_definition_body,
render_material_definition_list,
render_material_definition_term,
)
from ._mkdocstrings_autorefs import (
MKDOCSTRINGS_AUTOREFS_PREFIX,
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX,
Expand Down Expand Up @@ -33,13 +40,18 @@
"PYMD_CAPTIONS_PREFIX",
"PYMD_SNIPPET_PREFIX",
"PYTHON_MARKDOWN_ATTR_LIST_PREFIX",
"escape_deflist",
"material_admon_plugin",
"material_content_tabs_plugin",
"material_deflist_plugin",
"mkdocstrings_autorefs_plugin",
"mkdocstrings_crossreference_plugin",
"pymd_abbreviations_plugin",
"pymd_admon_plugin",
"pymd_captions_plugin",
"pymd_snippet_plugin",
"python_markdown_attr_list_plugin",
"render_material_definition_body",
"render_material_definition_list",
"render_material_definition_term",
)
111 changes: 111 additions & 0 deletions mdformat_mkdocs/mdit_plugins/_material_deflist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Material Definition Lists.

Based on
[mdformat-deflist](https://github.com/executablebooks/mdformat-deflist/blob/bbcf9ed4f80847db58b6f932ed95e2c7a6c49ae5/mdformat_deflist/plugin.py),
but modified for mkdocs-material conventions.

Example:
```md
`Lorem ipsum dolor sit amet`

: Sed sagittis eleifend rutrum. Donec vitae suscipit est. Nullam tempus
tellus non sem sollicitudin, quis rutrum leo facilisis.

`Cras arcu libero`

: Aliquam metus eros, pretium sed nulla venenatis, faucibus auctor ex. Proin
ut eros sed sapien ullamcorper consequat. Nunc ligula ante.

Duis mollis est eget nibh volutpat, fermentum aliquet dui mollis.
Name vulputate tincidunt fringilla.
Nullam dignissim ultrices urna non auctor.
```

Docs:
<https://squidfunk.github.io/mkdocs-material/reference/lists/#using-definition-lists>

"""

from __future__ import annotations

from typing import TYPE_CHECKING

from markdown_it import MarkdownIt
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


def material_deflist_plugin(md: MarkdownIt) -> None:
"""Add mkdocs-material definition list support to markdown-it parser."""
md.use(deflist_plugin)


def make_render_children(separator: str) -> Render:
"""Create a renderer that joins child nodes with a separator."""

def render_children(
node: RenderTreeNode,
context: RenderContext,
) -> str:
return separator.join(child.render(context) for child in node.children)

return render_children


def render_material_definition_list(
node: RenderTreeNode,
context: RenderContext,
) -> str:
"""Render Material Definition List."""
return make_render_children("\n")(node, context)


def render_material_definition_term(
node: RenderTreeNode,
context: RenderContext,
) -> str:
"""Render Material Definition Term."""
return make_render_children("\n")(node, context)


def render_material_definition_body(
node: RenderTreeNode,
context: RenderContext,
) -> str:
"""Render the definition body."""
tight_list = all(
child.type != "paragraph" or child.hidden for child in node.children
)
marker = ": " # FYI: increased for material
indent_width = len(marker)
context.env["indent_width"] += indent_width
try:
text = make_render_children("\n\n")(node, context)
lines = text.splitlines()
if not lines:
return ":"
indented_lines = [f"{marker}{lines[0]}"] + [
f"{' ' * indent_width}{line}" if line else "" for line in lines[1:]
]
joined_lines = ("" if tight_list else "\n") + "\n".join(indented_lines)
next_sibling = node.next_sibling
return joined_lines + (
"\n" if (next_sibling and next_sibling.type == "dt") else ""
)
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")
)
10 changes: 10 additions & 0 deletions mdformat_mkdocs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@
PYMD_CAPTIONS_PREFIX,
PYMD_SNIPPET_PREFIX,
PYTHON_MARKDOWN_ATTR_LIST_PREFIX,
escape_deflist,
material_admon_plugin,
material_content_tabs_plugin,
material_deflist_plugin,
mkdocstrings_autorefs_plugin,
mkdocstrings_crossreference_plugin,
pymd_abbreviations_plugin,
pymd_admon_plugin,
pymd_captions_plugin,
pymd_snippet_plugin,
python_markdown_attr_list_plugin,
render_material_definition_body,
render_material_definition_list,
render_material_definition_term,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -82,6 +87,7 @@ def update_mdit(mdit: MarkdownIt) -> None:
mdit.use(material_admon_plugin)
mdit.use(pymd_captions_plugin)
mdit.use(material_content_tabs_plugin)
mdit.use(material_deflist_plugin)
mdit.use(mkdocstrings_autorefs_plugin)
mdit.use(pymd_abbreviations_plugin)
mdit.use(pymd_admon_plugin)
Expand Down Expand Up @@ -205,6 +211,9 @@ def render_pymd_caption(node: RenderTreeNode, context: RenderContext) -> str:
"admonition_mkdocs_title": render_admon_title,
"content_tab_mkdocs": add_extra_admon_newline,
"content_tab_mkdocs_title": render_admon_title,
"dl": render_material_definition_list,
"dt": render_material_definition_term,
"dd": render_material_definition_body,
PYMD_CAPTIONS_PREFIX: render_pymd_caption,
MKDOCSTRINGS_AUTOREFS_PREFIX: _render_meta_content,
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
Expand All @@ -229,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,
}
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ recommended = [
"setuptools",
]
test = [
# "beartype >= 0.20.0rc0", # PLANNED: requires support for TYPE_CHECKING https://github.com/beartype/beartype/issues/477
"beartype >= 0.21.0",
"pytest >= 8.3.4",
# "pytest-beartype >= 0.1.1",
"pytest-cov >= 6.0.0",
"syrupy >= 4.7.2",
"pytest-beartype >= 0.2.0",
"pytest-cov >= 6.2.1",
"syrupy >= 4.9.1",
]

[project.urls]
Expand Down Expand Up @@ -196,7 +196,7 @@ extras = ["test"]

[tool.tox.env."py312-type"]
commands = [["mypy", "./mdformat_mkdocs", {default = [], extend = true, replace = "posargs"}]]
deps = ["mypy>=1.13.0"]
deps = ["mypy>=1.17.1"]

[tool.tox.env."py39-hook"]
commands = [["pre-commit", "run", "--config=.pre-commit-test.yaml", "--all-files", {default = ["--show-diff-on-failure", "--verbose"], extend = true, replace = "posargs"}]]
Expand Down
179 changes: 179 additions & 0 deletions tests/format/fixtures/material_deflist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
Pandoc (with slightly changed indents):
.
paragraph

Term 1

: Definition 1

Term 2 with *inline markup*

: Definition 2

{ some code, part of Definition 2 }

Third paragraph of definition 2.

paragraph
.
paragraph

Term 1

: Definition 1

Term 2 with *inline markup*

: Definition 2

```
{ some code, part of Definition 2 }
```

Third paragraph of definition 2.

paragraph
.

Pandoc 2:
.
Term 1

: Definition
with lazy continuation.

Second paragraph of the definition.
.
Term 1

: Definition
with lazy continuation.

Second paragraph of the definition.
.

Pandoc 3
.
paragraph

Term 1
~ Definition 1

Term 2
~ Definition 2a
~ Definition 2b

paragraph
.
paragraph

Term 1
: Definition 1

Term 2
: Definition 2a
: Definition 2b

paragraph
.

Spaces after a colon:
.
Term 1
: paragraph

Term 2
: code block
.
Term 1
: paragraph

Term 2
: ```
code block
```
.

List is tight, only if all dts are tight:
.
Term 1
: foo
: bar

Term 2
: foo

: bar
.
Term 1

: foo

: bar

Term 2

: foo

: bar
.


Regression test (first paragraphs shouldn't be tight):
.
Term 1
: foo

bar

Term 2
: foo
.
Term 1

: foo

bar

Term 2

: foo
.

Nested definition lists:

.
test
: foo
: bar
: baz
: bar
: foo
.
test
: foo
: bar
: baz
: bar
: foo
.

Regression test (blockquote inside deflist)
.
foo
: > bar
: baz
.
foo
: > bar
: baz
.

Escaped deflist
.
Term 1
\: Definition
.
Term 1
\: Definition
.
Loading
Loading