Skip to content

Commit b9a5e1a

Browse files
KyleKingCopilot
andauthored
fix(#54): add 4-space indented deflists (#55)
Co-authored-by: Copilot <[email protected]>
1 parent 18a85b6 commit b9a5e1a

File tree

9 files changed

+376
-5
lines changed

9 files changed

+376
-5
lines changed

mdformat_mkdocs/mdit_plugins/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
MATERIAL_CONTENT_TAB_MARKERS,
66
material_content_tabs_plugin,
77
)
8+
from ._material_deflist import (
9+
escape_deflist,
10+
material_deflist_plugin,
11+
render_material_definition_body,
12+
render_material_definition_list,
13+
render_material_definition_term,
14+
)
815
from ._mkdocstrings_autorefs import (
916
MKDOCSTRINGS_AUTOREFS_PREFIX,
1017
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX,
@@ -33,13 +40,18 @@
3340
"PYMD_CAPTIONS_PREFIX",
3441
"PYMD_SNIPPET_PREFIX",
3542
"PYTHON_MARKDOWN_ATTR_LIST_PREFIX",
43+
"escape_deflist",
3644
"material_admon_plugin",
3745
"material_content_tabs_plugin",
46+
"material_deflist_plugin",
3847
"mkdocstrings_autorefs_plugin",
3948
"mkdocstrings_crossreference_plugin",
4049
"pymd_abbreviations_plugin",
4150
"pymd_admon_plugin",
4251
"pymd_captions_plugin",
4352
"pymd_snippet_plugin",
4453
"python_markdown_attr_list_plugin",
54+
"render_material_definition_body",
55+
"render_material_definition_list",
56+
"render_material_definition_term",
4557
)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""Material Definition Lists.
2+
3+
Based on
4+
[mdformat-deflist](https://github.com/executablebooks/mdformat-deflist/blob/bbcf9ed4f80847db58b6f932ed95e2c7a6c49ae5/mdformat_deflist/plugin.py),
5+
but modified for mkdocs-material conventions.
6+
7+
Example:
8+
```md
9+
`Lorem ipsum dolor sit amet`
10+
11+
: Sed sagittis eleifend rutrum. Donec vitae suscipit est. Nullam tempus
12+
tellus non sem sollicitudin, quis rutrum leo facilisis.
13+
14+
`Cras arcu libero`
15+
16+
: Aliquam metus eros, pretium sed nulla venenatis, faucibus auctor ex. Proin
17+
ut eros sed sapien ullamcorper consequat. Nunc ligula ante.
18+
19+
Duis mollis est eget nibh volutpat, fermentum aliquet dui mollis.
20+
Nam vulputate tincidunt fringilla.
21+
Nullam dignissim ultrices urna non auctor.
22+
```
23+
24+
Docs:
25+
<https://squidfunk.github.io/mkdocs-material/reference/lists/#using-definition-lists>
26+
27+
"""
28+
29+
from __future__ import annotations
30+
31+
from typing import TYPE_CHECKING
32+
33+
from markdown_it import MarkdownIt
34+
from mdit_py_plugins.deflist import deflist_plugin
35+
36+
if TYPE_CHECKING:
37+
from markdown_it import MarkdownIt
38+
from mdformat.renderer import RenderContext, RenderTreeNode
39+
from mdformat.renderer.typing import Render
40+
41+
42+
def material_deflist_plugin(md: MarkdownIt) -> None:
43+
"""Add mkdocs-material definition list support to markdown-it parser."""
44+
md.use(deflist_plugin)
45+
46+
47+
def make_render_children(separator: str) -> Render:
48+
"""Create a renderer that joins child nodes with a separator."""
49+
50+
def render_children(
51+
node: RenderTreeNode,
52+
context: RenderContext,
53+
) -> str:
54+
return separator.join(child.render(context) for child in node.children)
55+
56+
return render_children
57+
58+
59+
def render_material_definition_list(
60+
node: RenderTreeNode,
61+
context: RenderContext,
62+
) -> str:
63+
"""Render Material Definition List."""
64+
return make_render_children("\n")(node, context)
65+
66+
67+
def render_material_definition_term(
68+
node: RenderTreeNode,
69+
context: RenderContext,
70+
) -> str:
71+
"""Render Material Definition Term."""
72+
return make_render_children("\n")(node, context)
73+
74+
75+
def render_material_definition_body(
76+
node: RenderTreeNode,
77+
context: RenderContext,
78+
) -> str:
79+
"""Render the definition body."""
80+
tight_list = all(
81+
child.type != "paragraph" or child.hidden for child in node.children
82+
)
83+
marker = ": " # FYI: increased for material
84+
indent_width = len(marker)
85+
context.env["indent_width"] += indent_width
86+
try:
87+
text = make_render_children("\n\n")(node, context)
88+
lines = text.splitlines()
89+
if not lines:
90+
return ":"
91+
indented_lines = [f"{marker}{lines[0]}"] + [
92+
f"{' ' * indent_width}{line}" if line else "" for line in lines[1:]
93+
]
94+
joined_lines = ("" if tight_list else "\n") + "\n".join(indented_lines)
95+
next_sibling = node.next_sibling
96+
return joined_lines + (
97+
"\n" if (next_sibling and next_sibling.type == "dt") else ""
98+
)
99+
finally:
100+
context.env["indent_width"] -= indent_width
101+
102+
103+
def escape_deflist(
104+
text: str,
105+
node: RenderTreeNode, # noqa: ARG001
106+
context: RenderContext, # noqa: ARG001
107+
) -> str:
108+
"""Escape line starting ":" which would otherwise be parsed as a definition list."""
109+
return "\n".join(
110+
"\\" + line if line.startswith(":") else line for line in text.split("\n")
111+
)

mdformat_mkdocs/plugin.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@
1919
PYMD_CAPTIONS_PREFIX,
2020
PYMD_SNIPPET_PREFIX,
2121
PYTHON_MARKDOWN_ATTR_LIST_PREFIX,
22+
escape_deflist,
2223
material_admon_plugin,
2324
material_content_tabs_plugin,
25+
material_deflist_plugin,
2426
mkdocstrings_autorefs_plugin,
2527
mkdocstrings_crossreference_plugin,
2628
pymd_abbreviations_plugin,
2729
pymd_admon_plugin,
2830
pymd_captions_plugin,
2931
pymd_snippet_plugin,
3032
python_markdown_attr_list_plugin,
33+
render_material_definition_body,
34+
render_material_definition_list,
35+
render_material_definition_term,
3136
)
3237

3338
if TYPE_CHECKING:
@@ -82,6 +87,7 @@ def update_mdit(mdit: MarkdownIt) -> None:
8287
mdit.use(material_admon_plugin)
8388
mdit.use(pymd_captions_plugin)
8489
mdit.use(material_content_tabs_plugin)
90+
mdit.use(material_deflist_plugin)
8591
mdit.use(mkdocstrings_autorefs_plugin)
8692
mdit.use(pymd_abbreviations_plugin)
8793
mdit.use(pymd_admon_plugin)
@@ -205,6 +211,9 @@ def render_pymd_caption(node: RenderTreeNode, context: RenderContext) -> str:
205211
"admonition_mkdocs_title": render_admon_title,
206212
"content_tab_mkdocs": add_extra_admon_newline,
207213
"content_tab_mkdocs_title": render_admon_title,
214+
"dl": render_material_definition_list,
215+
"dt": render_material_definition_term,
216+
"dd": render_material_definition_body,
208217
PYMD_CAPTIONS_PREFIX: render_pymd_caption,
209218
MKDOCSTRINGS_AUTOREFS_PREFIX: _render_meta_content,
210219
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
@@ -229,4 +238,5 @@ def render_pymd_caption(node: RenderTreeNode, context: RenderContext) -> str:
229238
"bullet_list": normalize_list,
230239
"inline": postprocess_list_wrap, # type: ignore[has-type]
231240
"ordered_list": normalize_list,
241+
"paragraph": escape_deflist,
232242
}

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ recommended = [
4343
"setuptools",
4444
]
4545
test = [
46-
# "beartype >= 0.20.0rc0", # PLANNED: requires support for TYPE_CHECKING https://github.com/beartype/beartype/issues/477
46+
"beartype >= 0.21.0",
4747
"pytest >= 8.3.4",
48-
# "pytest-beartype >= 0.1.1",
49-
"pytest-cov >= 6.0.0",
50-
"syrupy >= 4.7.2",
48+
"pytest-beartype >= 0.2.0",
49+
"pytest-cov >= 6.2.1",
50+
"syrupy >= 4.9.1",
5151
]
5252

5353
[project.urls]
@@ -196,7 +196,7 @@ extras = ["test"]
196196

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

201201
[tool.tox.env."py39-hook"]
202202
commands = [["pre-commit", "run", "--config=.pre-commit-test.yaml", "--all-files", {default = ["--show-diff-on-failure", "--verbose"], extend = true, replace = "posargs"}]]
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
Pandoc (with slightly changed indents):
2+
.
3+
paragraph
4+
5+
Term 1
6+
7+
: Definition 1
8+
9+
Term 2 with *inline markup*
10+
11+
: Definition 2
12+
13+
{ some code, part of Definition 2 }
14+
15+
Third paragraph of definition 2.
16+
17+
paragraph
18+
.
19+
paragraph
20+
21+
Term 1
22+
23+
: Definition 1
24+
25+
Term 2 with *inline markup*
26+
27+
: Definition 2
28+
29+
```
30+
{ some code, part of Definition 2 }
31+
```
32+
33+
Third paragraph of definition 2.
34+
35+
paragraph
36+
.
37+
38+
Pandoc 2:
39+
.
40+
Term 1
41+
42+
: Definition
43+
with lazy continuation.
44+
45+
Second paragraph of the definition.
46+
.
47+
Term 1
48+
49+
: Definition
50+
with lazy continuation.
51+
52+
Second paragraph of the definition.
53+
.
54+
55+
Pandoc 3
56+
.
57+
paragraph
58+
59+
Term 1
60+
~ Definition 1
61+
62+
Term 2
63+
~ Definition 2a
64+
~ Definition 2b
65+
66+
paragraph
67+
.
68+
paragraph
69+
70+
Term 1
71+
: Definition 1
72+
73+
Term 2
74+
: Definition 2a
75+
: Definition 2b
76+
77+
paragraph
78+
.
79+
80+
Spaces after a colon:
81+
.
82+
Term 1
83+
: paragraph
84+
85+
Term 2
86+
: code block
87+
.
88+
Term 1
89+
: paragraph
90+
91+
Term 2
92+
: ```
93+
code block
94+
```
95+
.
96+
97+
List is tight, only if all dts are tight:
98+
.
99+
Term 1
100+
: foo
101+
: bar
102+
103+
Term 2
104+
: foo
105+
106+
: bar
107+
.
108+
Term 1
109+
110+
: foo
111+
112+
: bar
113+
114+
Term 2
115+
116+
: foo
117+
118+
: bar
119+
.
120+
121+
122+
Regression test (first paragraphs shouldn't be tight):
123+
.
124+
Term 1
125+
: foo
126+
127+
bar
128+
129+
Term 2
130+
: foo
131+
.
132+
Term 1
133+
134+
: foo
135+
136+
bar
137+
138+
Term 2
139+
140+
: foo
141+
.
142+
143+
Nested definition lists:
144+
145+
.
146+
test
147+
: foo
148+
: bar
149+
: baz
150+
: bar
151+
: foo
152+
.
153+
test
154+
: foo
155+
: bar
156+
: baz
157+
: bar
158+
: foo
159+
.
160+
161+
Regression test (blockquote inside deflist)
162+
.
163+
foo
164+
: > bar
165+
: baz
166+
.
167+
foo
168+
: > bar
169+
: baz
170+
.
171+
172+
Escaped deflist
173+
.
174+
Term 1
175+
\: Definition
176+
.
177+
Term 1
178+
\: Definition
179+
.

0 commit comments

Comments
 (0)