mdformat-mkdocs 5.1.4__tar.gz → 5.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/PKG-INFO +21 -1
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/README.md +19 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/__init__.py +2 -1
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_helpers.py +1 -1
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_normalize_list.py +77 -17
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/admon_factories/_whitespace_admon_factories.py +4 -2
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/__init__.py +11 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_material_deflist.py +0 -1
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_mkdocstrings_autorefs.py +1 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_mkdocstrings_crossreference.py +1 -0
- mdformat_mkdocs-5.2.0/mdformat_mkdocs/mdit_plugins/_mkdocstrings_injection.py +78 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_python_markdown_attr_list.py +6 -5
- mdformat_mkdocs-5.2.0/mdformat_mkdocs/mdit_plugins/_spaced_url_link.py +78 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/plugin.py +116 -40
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/pyproject.toml +10 -2
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/LICENSE +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_postprocess_inline.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/__init__.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/admon_factories/README.md +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/admon_factories/__init__.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_material_admon.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_material_content_tabs.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_abbreviations.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_admon.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_arithmatex.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_captions.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_snippet.py +0 -0
- {mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdformat_mkdocs
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0
|
|
4
4
|
Summary: An mdformat plugin for mkdocs and Material for MkDocs
|
|
5
5
|
Keywords: markdown,markdown-it,mdformat,mdformat_plugin_template
|
|
6
6
|
Author: kyleking
|
|
@@ -32,6 +32,7 @@ Requires-Dist: mdformat-hooks>=0.1.0 ; extra == 'recommended-mdsf'
|
|
|
32
32
|
Requires-Dist: mdformat-simple-breaks>=0.0.1 ; extra == 'recommended-mdsf'
|
|
33
33
|
Requires-Dist: mdformat-wikilink>=0.2.0 ; extra == 'recommended-mdsf'
|
|
34
34
|
Requires-Dist: beartype>=0.21.0 ; extra == 'test'
|
|
35
|
+
Requires-Dist: hypothesis>=6.100.0 ; extra == 'test'
|
|
35
36
|
Requires-Dist: pytest>=9.0.1 ; extra == 'test'
|
|
36
37
|
Requires-Dist: pytest-beartype>=0.2.0 ; extra == 'test'
|
|
37
38
|
Requires-Dist: pytest-cov>=7.0.0 ; extra == 'test'
|
|
@@ -62,6 +63,8 @@ Supports:
|
|
|
62
63
|
- [MkDocs-Material Content Tabs\*](https://squidfunk.github.io/mkdocs-material/reference/content-tabs)
|
|
63
64
|
- \*Note: the markup (HTML) rendered by this plugin is sufficient for formatting but not for viewing in a browser. Please open an issue if you have a need to generate valid HTML.
|
|
64
65
|
- [MkDocs-Material Definition Lists](https://squidfunk.github.io/mkdocs-material/reference/lists/#using-definition-lists)
|
|
66
|
+
- [mkdocstrings Injection Blocks](https://mkdocstrings.github.io/usage/)
|
|
67
|
+
- Preserves `:::` identifier blocks and their indented YAML options verbatim, including when nested inside lists with `--align-semantic-breaks-in-lists`
|
|
65
68
|
- [mkdocstrings Anchors (autorefs)](https://mkdocstrings.github.io/autorefs/#markdown-anchors)
|
|
66
69
|
- [mkdocstrings Cross-References](https://mkdocstrings.github.io/usage/#cross-references)
|
|
67
70
|
- [Python Markdown "Abbreviations"\*](https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-abbreviations)
|
|
@@ -77,6 +80,23 @@ Supports:
|
|
|
77
80
|
- [Python Markdown "Snippets"\*](https://facelessuser.github.io/pymdown-extensions/extensions/snippets)
|
|
78
81
|
- \*Note: the markup (HTML) renders the plain text without implementing the snippet logic. I'm open to contributions if anyone needs full support for snippets
|
|
79
82
|
|
|
83
|
+
### Features with Implicit Support
|
|
84
|
+
|
|
85
|
+
The following MkDocs/Material/PyMdown syntax passes through mdformat-mkdocs unchanged — it is not modified or corrupted, but it is also not actively normalized:
|
|
86
|
+
|
|
87
|
+
- [PyMdown Keys](https://facelessuser.github.io/pymdown-extensions/extensions/keys/) — `++ctrl+alt+del++` (not a markdown construct, preserved as-is)
|
|
88
|
+
- [PyMdown Critic Markup](https://facelessuser.github.io/pymdown-extensions/extensions/critic/) — `{--deleted--}`, `{++added++}`, `{~~old~>new~~}`, `{==highlight==}`, `{>>comment<<}`
|
|
89
|
+
- [PyMdown Highlight](https://facelessuser.github.io/pymdown-extensions/extensions/mark/) — `==marked text==`
|
|
90
|
+
- [PyMdown Caret / Tilde](https://facelessuser.github.io/pymdown-extensions/extensions/caret/) — `H^2^O`, `CH~3~OH`
|
|
91
|
+
- [PyMdown Emoji](https://facelessuser.github.io/pymdown-extensions/extensions/emoji/) — `:smile:`, `:material-icon:`
|
|
92
|
+
- [PyMdown InlineHilite](https://facelessuser.github.io/pymdown-extensions/extensions/inlinehilite/) — language hints inside backtick spans (`:::python code`) are never modified
|
|
93
|
+
- [PyMdown SmartSymbols](https://facelessuser.github.io/pymdown-extensions/extensions/smartsymbols/) — `(c)`, `(tm)`, `--`, `-->` are plain ASCII in source and not touched
|
|
94
|
+
- [PyMdown MagicLink](https://facelessuser.github.io/pymdown-extensions/extensions/magiclink/) — `@username`, `#123` are plain text and pass through
|
|
95
|
+
- [Material Grids](https://squidfunk.github.io/mkdocs-material/reference/grids/) — `<div class="grid cards" markdown>` is an HTML block; content is preserved but markdown inside is not reformatted
|
|
96
|
+
- [Mermaid / Superfences](https://squidfunk.github.io/mkdocs-material/reference/diagrams/) — diagram code inside fenced blocks is never modified
|
|
97
|
+
|
|
98
|
+
**Note on [PyMdown ProgressBar](https://facelessuser.github.io/pymdown-extensions/extensions/progressbar/)**: The syntax `[=50% "50%"]` resembles an undefined link reference and will be escaped to `\[=50% "50%"\]` by default. Use `--ignore-missing-references` to preserve it, or avoid this extension if you use mdformat without that flag.
|
|
99
|
+
|
|
80
100
|
See the example test files, [./tests/pre-commit-test.md](https://raw.githubusercontent.com/KyleKing/mdformat-mkdocs/main/tests/pre-commit-test.md) and [./tests/format/fixtures.md](https://raw.githubusercontent.com/KyleKing/mdformat-mkdocs/main/tests/format/fixtures.md)
|
|
81
101
|
|
|
82
102
|
## `mdformat` Usage
|
|
@@ -15,6 +15,8 @@ Supports:
|
|
|
15
15
|
- [MkDocs-Material Content Tabs\*](https://squidfunk.github.io/mkdocs-material/reference/content-tabs)
|
|
16
16
|
- \*Note: the markup (HTML) rendered by this plugin is sufficient for formatting but not for viewing in a browser. Please open an issue if you have a need to generate valid HTML.
|
|
17
17
|
- [MkDocs-Material Definition Lists](https://squidfunk.github.io/mkdocs-material/reference/lists/#using-definition-lists)
|
|
18
|
+
- [mkdocstrings Injection Blocks](https://mkdocstrings.github.io/usage/)
|
|
19
|
+
- Preserves `:::` identifier blocks and their indented YAML options verbatim, including when nested inside lists with `--align-semantic-breaks-in-lists`
|
|
18
20
|
- [mkdocstrings Anchors (autorefs)](https://mkdocstrings.github.io/autorefs/#markdown-anchors)
|
|
19
21
|
- [mkdocstrings Cross-References](https://mkdocstrings.github.io/usage/#cross-references)
|
|
20
22
|
- [Python Markdown "Abbreviations"\*](https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-abbreviations)
|
|
@@ -30,6 +32,23 @@ Supports:
|
|
|
30
32
|
- [Python Markdown "Snippets"\*](https://facelessuser.github.io/pymdown-extensions/extensions/snippets)
|
|
31
33
|
- \*Note: the markup (HTML) renders the plain text without implementing the snippet logic. I'm open to contributions if anyone needs full support for snippets
|
|
32
34
|
|
|
35
|
+
### Features with Implicit Support
|
|
36
|
+
|
|
37
|
+
The following MkDocs/Material/PyMdown syntax passes through mdformat-mkdocs unchanged — it is not modified or corrupted, but it is also not actively normalized:
|
|
38
|
+
|
|
39
|
+
- [PyMdown Keys](https://facelessuser.github.io/pymdown-extensions/extensions/keys/) — `++ctrl+alt+del++` (not a markdown construct, preserved as-is)
|
|
40
|
+
- [PyMdown Critic Markup](https://facelessuser.github.io/pymdown-extensions/extensions/critic/) — `{--deleted--}`, `{++added++}`, `{~~old~>new~~}`, `{==highlight==}`, `{>>comment<<}`
|
|
41
|
+
- [PyMdown Highlight](https://facelessuser.github.io/pymdown-extensions/extensions/mark/) — `==marked text==`
|
|
42
|
+
- [PyMdown Caret / Tilde](https://facelessuser.github.io/pymdown-extensions/extensions/caret/) — `H^2^O`, `CH~3~OH`
|
|
43
|
+
- [PyMdown Emoji](https://facelessuser.github.io/pymdown-extensions/extensions/emoji/) — `:smile:`, `:material-icon:`
|
|
44
|
+
- [PyMdown InlineHilite](https://facelessuser.github.io/pymdown-extensions/extensions/inlinehilite/) — language hints inside backtick spans (`:::python code`) are never modified
|
|
45
|
+
- [PyMdown SmartSymbols](https://facelessuser.github.io/pymdown-extensions/extensions/smartsymbols/) — `(c)`, `(tm)`, `--`, `-->` are plain ASCII in source and not touched
|
|
46
|
+
- [PyMdown MagicLink](https://facelessuser.github.io/pymdown-extensions/extensions/magiclink/) — `@username`, `#123` are plain text and pass through
|
|
47
|
+
- [Material Grids](https://squidfunk.github.io/mkdocs-material/reference/grids/) — `<div class="grid cards" markdown>` is an HTML block; content is preserved but markdown inside is not reformatted
|
|
48
|
+
- [Mermaid / Superfences](https://squidfunk.github.io/mkdocs-material/reference/diagrams/) — diagram code inside fenced blocks is never modified
|
|
49
|
+
|
|
50
|
+
**Note on [PyMdown ProgressBar](https://facelessuser.github.io/pymdown-extensions/extensions/progressbar/)**: The syntax `[=50% "50%"]` resembles an undefined link reference and will be escaped to `\[=50% "50%"\]` by default. Use `--ignore-missing-references` to preserve it, or avoid this extension if you use mdformat without that flag.
|
|
51
|
+
|
|
33
52
|
See the example test files, [./tests/pre-commit-test.md](https://raw.githubusercontent.com/KyleKing/mdformat-mkdocs/main/tests/pre-commit-test.md) and [./tests/format/fixtures.md](https://raw.githubusercontent.com/KyleKing/mdformat-mkdocs/main/tests/format/fixtures.md)
|
|
34
53
|
|
|
35
54
|
## `mdformat` Usage
|
|
@@ -19,7 +19,11 @@ from ._helpers import (
|
|
|
19
19
|
rstrip_result,
|
|
20
20
|
separate_indent,
|
|
21
21
|
)
|
|
22
|
-
from .mdit_plugins import
|
|
22
|
+
from .mdit_plugins import (
|
|
23
|
+
INJECTION_PATTERN,
|
|
24
|
+
MATERIAL_ADMON_MARKERS,
|
|
25
|
+
MATERIAL_CONTENT_TAB_MARKERS,
|
|
26
|
+
)
|
|
23
27
|
|
|
24
28
|
if TYPE_CHECKING:
|
|
25
29
|
from collections.abc import Mapping
|
|
@@ -71,6 +75,7 @@ class Syntax(Enum):
|
|
|
71
75
|
START_MARKED = "START_MARKED"
|
|
72
76
|
EDGE_CODE = "EDGE_CODE"
|
|
73
77
|
HTML = "HTML"
|
|
78
|
+
INJECTION = "INJECTION"
|
|
74
79
|
|
|
75
80
|
@classmethod
|
|
76
81
|
def from_content(cls, content: str) -> Syntax | None:
|
|
@@ -85,18 +90,25 @@ class Syntax(Enum):
|
|
|
85
90
|
if match["item"].startswith("```"):
|
|
86
91
|
return cls.CODE_NUMBERED if is_numbered else cls.CODE_BULLETED
|
|
87
92
|
return cls.LIST_NUMBERED if is_numbered else cls.LIST_BULLETED
|
|
93
|
+
|
|
94
|
+
result = None
|
|
88
95
|
if any(content.startswith(f"{marker} ") for marker in MARKERS):
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
result = cls.START_MARKED
|
|
97
|
+
elif content.startswith("```"):
|
|
98
|
+
result = cls.EDGE_CODE
|
|
99
|
+
elif content.startswith("<"):
|
|
100
|
+
result = cls.HTML
|
|
101
|
+
elif INJECTION_PATTERN.match(content):
|
|
102
|
+
result = cls.INJECTION
|
|
103
|
+
return result
|
|
95
104
|
|
|
96
105
|
|
|
97
106
|
SYNTAX_CODE_LIST = {Syntax.CODE_BULLETED, Syntax.CODE_NUMBERED}
|
|
98
107
|
"""The start of a code block, which is also the start of a list."""
|
|
99
108
|
|
|
109
|
+
SYNTAX_LIST = {Syntax.LIST_BULLETED, Syntax.LIST_NUMBERED, *SYNTAX_CODE_LIST}
|
|
110
|
+
"""Any line that begins a list item."""
|
|
111
|
+
|
|
100
112
|
|
|
101
113
|
class ParsedLine(NamedTuple):
|
|
102
114
|
"""Parsed Line of text."""
|
|
@@ -212,7 +224,7 @@ class BlockIndent(NamedTuple):
|
|
|
212
224
|
start_line: int
|
|
213
225
|
raw_indent: str
|
|
214
226
|
indent_depth: int
|
|
215
|
-
kind: Literal["code", "HTML"]
|
|
227
|
+
kind: Literal["code", "HTML", "injection"]
|
|
216
228
|
|
|
217
229
|
|
|
218
230
|
def _parse_code_block(last: BlockIndent | None, line: LineResult) -> BlockIndent | None:
|
|
@@ -251,9 +263,44 @@ def _parse_html_line(last: BlockIndent | None, line: LineResult) -> BlockIndent
|
|
|
251
263
|
elif last and not line.parsed.content:
|
|
252
264
|
# Stop tracking an HTML block on a line break
|
|
253
265
|
result = None
|
|
266
|
+
elif (
|
|
267
|
+
last
|
|
268
|
+
and line.parsed.syntax in SYNTAX_LIST
|
|
269
|
+
and len(line.parsed.indent) < len(last.raw_indent)
|
|
270
|
+
):
|
|
271
|
+
# A list item that dedents below the block is a sibling, not HTML
|
|
272
|
+
# content. This guards against an inline autolink (or comment) on a
|
|
273
|
+
# continuation line being mistaken for a block that swallows siblings.
|
|
274
|
+
result = None
|
|
254
275
|
return result
|
|
255
276
|
|
|
256
277
|
|
|
278
|
+
def _parse_injection_block(
|
|
279
|
+
last: BlockIndent | None,
|
|
280
|
+
line: LineResult,
|
|
281
|
+
) -> BlockIndent | None:
|
|
282
|
+
"""Identify mkdocstrings injection sections."""
|
|
283
|
+
if last is not None:
|
|
284
|
+
if line.parsed.content and len(line.parsed.indent) <= len(last.raw_indent):
|
|
285
|
+
if line.parsed.syntax == Syntax.INJECTION:
|
|
286
|
+
return BlockIndent(
|
|
287
|
+
start_line=line.parsed.line_num,
|
|
288
|
+
raw_indent=line.parsed.indent,
|
|
289
|
+
indent_depth=len(line.parents),
|
|
290
|
+
kind="injection",
|
|
291
|
+
)
|
|
292
|
+
return None
|
|
293
|
+
return last
|
|
294
|
+
if line.parsed.syntax == Syntax.INJECTION:
|
|
295
|
+
return BlockIndent(
|
|
296
|
+
start_line=line.parsed.line_num,
|
|
297
|
+
raw_indent=line.parsed.indent,
|
|
298
|
+
indent_depth=len(line.parents),
|
|
299
|
+
kind="injection",
|
|
300
|
+
)
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
|
|
257
304
|
# ======================================================================================
|
|
258
305
|
# Semantic Indent Handling
|
|
259
306
|
|
|
@@ -274,12 +321,11 @@ def _parse_semantic_indent(
|
|
|
274
321
|
tin: tuple[LineResult, BlockIndent | None],
|
|
275
322
|
) -> SemanticIndent:
|
|
276
323
|
"""Conditionally evaluate when semantic indents are necessary."""
|
|
277
|
-
|
|
278
|
-
line, code_indent = tin
|
|
324
|
+
line, block_indent = tin
|
|
279
325
|
|
|
280
326
|
if (
|
|
281
327
|
not line.parsed.content
|
|
282
|
-
or
|
|
328
|
+
or block_indent is not None
|
|
283
329
|
or line.parsed.syntax in SYNTAX_CODE_LIST
|
|
284
330
|
):
|
|
285
331
|
result = SemanticIndent.EMPTY
|
|
@@ -420,22 +466,36 @@ def parse_text(
|
|
|
420
466
|
indent if (indent and code_indents[indent.start_line] is None) else None
|
|
421
467
|
for indent in map_lookback(_parse_html_line, lines, None)
|
|
422
468
|
]
|
|
423
|
-
|
|
469
|
+
injection_indents = [
|
|
470
|
+
# Any indents initiated from within a `code_block_indents` should be ignored
|
|
471
|
+
indent if (indent and code_indents[indent.start_line] is None) else None
|
|
472
|
+
for indent in map_lookback(_parse_injection_block, lines, None)
|
|
473
|
+
]
|
|
474
|
+
# When multiple match, code_indents take precedence, then html_indents
|
|
424
475
|
block_indents = [
|
|
425
|
-
c_ or h_
|
|
476
|
+
c_ or h_ or i_
|
|
477
|
+
for c_, h_, i_ in zip(
|
|
478
|
+
code_indents, html_indents, injection_indents, strict=True
|
|
479
|
+
)
|
|
426
480
|
]
|
|
427
481
|
new_indents = [*starmap(_format_new_indent, zip(lines, block_indents, strict=True))]
|
|
428
482
|
|
|
429
483
|
new_contents = [
|
|
430
|
-
_format_new_content(
|
|
431
|
-
|
|
484
|
+
_format_new_content(
|
|
485
|
+
line,
|
|
486
|
+
inc_numbers,
|
|
487
|
+
block_indent is not None and block_indent.kind in {"code", "injection"},
|
|
488
|
+
)
|
|
489
|
+
for line, block_indent in zip(lines, block_indents, strict=True)
|
|
432
490
|
]
|
|
433
491
|
|
|
434
492
|
if use_sem_break:
|
|
435
493
|
semantic_indents = map_lookback(
|
|
436
494
|
_parse_semantic_indent,
|
|
437
|
-
[*zip(lines,
|
|
438
|
-
_parse_semantic_indent(
|
|
495
|
+
[*zip(lines, block_indents, strict=True)],
|
|
496
|
+
_parse_semantic_indent(
|
|
497
|
+
SemanticIndent.INITIAL, (lines[0], block_indents[0])
|
|
498
|
+
),
|
|
439
499
|
)
|
|
440
500
|
new_indents = [
|
|
441
501
|
_trim_semantic_indent(indent, s_i, in_defbody)
|
|
@@ -189,8 +189,10 @@ def new_token(
|
|
|
189
189
|
kind: str,
|
|
190
190
|
) -> Generator[Token, None, None]:
|
|
191
191
|
"""Create scoped token."""
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
try:
|
|
193
|
+
yield state.push(f"{name}_open", kind, 1)
|
|
194
|
+
finally:
|
|
195
|
+
state.push(f"{name}_close", kind, -1)
|
|
194
196
|
|
|
195
197
|
|
|
196
198
|
def default_render(
|
|
@@ -21,6 +21,11 @@ from ._mkdocstrings_crossreference import (
|
|
|
21
21
|
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
|
|
22
22
|
mkdocstrings_crossreference_plugin,
|
|
23
23
|
)
|
|
24
|
+
from ._mkdocstrings_injection import (
|
|
25
|
+
INJECTION_PATTERN,
|
|
26
|
+
MKDOCSTRINGS_INJECTION_PREFIX,
|
|
27
|
+
mkdocstrings_injection_plugin,
|
|
28
|
+
)
|
|
24
29
|
from ._pymd_abbreviations import PYMD_ABBREVIATIONS_PREFIX, pymd_abbreviations_plugin
|
|
25
30
|
from ._pymd_admon import pymd_admon_plugin
|
|
26
31
|
from ._pymd_arithmatex import (
|
|
@@ -37,21 +42,25 @@ from ._python_markdown_attr_list import (
|
|
|
37
42
|
PYTHON_MARKDOWN_ATTR_LIST_PREFIX,
|
|
38
43
|
python_markdown_attr_list_plugin,
|
|
39
44
|
)
|
|
45
|
+
from ._spaced_url_link import SPACED_URL_LINK_PREFIX, spaced_url_link_plugin
|
|
40
46
|
|
|
41
47
|
__all__ = (
|
|
42
48
|
"AMSMATH_BLOCK",
|
|
43
49
|
"DOLLARMATH_BLOCK",
|
|
44
50
|
"DOLLARMATH_BLOCK_LABEL",
|
|
45
51
|
"DOLLARMATH_INLINE",
|
|
52
|
+
"INJECTION_PATTERN",
|
|
46
53
|
"MATERIAL_ADMON_MARKERS",
|
|
47
54
|
"MATERIAL_CONTENT_TAB_MARKERS",
|
|
48
55
|
"MKDOCSTRINGS_AUTOREFS_PREFIX",
|
|
49
56
|
"MKDOCSTRINGS_CROSSREFERENCE_PREFIX",
|
|
50
57
|
"MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX",
|
|
58
|
+
"MKDOCSTRINGS_INJECTION_PREFIX",
|
|
51
59
|
"PYMD_ABBREVIATIONS_PREFIX",
|
|
52
60
|
"PYMD_CAPTIONS_PREFIX",
|
|
53
61
|
"PYMD_SNIPPET_PREFIX",
|
|
54
62
|
"PYTHON_MARKDOWN_ATTR_LIST_PREFIX",
|
|
63
|
+
"SPACED_URL_LINK_PREFIX",
|
|
55
64
|
"TEXMATH_BLOCK_EQNO",
|
|
56
65
|
"escape_deflist",
|
|
57
66
|
"material_admon_plugin",
|
|
@@ -59,6 +68,7 @@ __all__ = (
|
|
|
59
68
|
"material_deflist_plugin",
|
|
60
69
|
"mkdocstrings_autorefs_plugin",
|
|
61
70
|
"mkdocstrings_crossreference_plugin",
|
|
71
|
+
"mkdocstrings_injection_plugin",
|
|
62
72
|
"pymd_abbreviations_plugin",
|
|
63
73
|
"pymd_admon_plugin",
|
|
64
74
|
"pymd_arithmatex_plugin",
|
|
@@ -68,4 +78,5 @@ __all__ = (
|
|
|
68
78
|
"render_material_definition_body",
|
|
69
79
|
"render_material_definition_list",
|
|
70
80
|
"render_material_definition_term",
|
|
81
|
+
"spaced_url_link_plugin",
|
|
71
82
|
)
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_material_deflist.py
RENAMED
|
@@ -35,7 +35,6 @@ from markdown_it import MarkdownIt
|
|
|
35
35
|
from mdit_py_plugins.deflist import deflist_plugin
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
|
-
from markdown_it import MarkdownIt
|
|
39
38
|
from mdformat.renderer import RenderContext, RenderTreeNode
|
|
40
39
|
from mdformat.renderer.typing import Render
|
|
41
40
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""mkdocstrings injection blocks.
|
|
2
|
+
|
|
3
|
+
Matches:
|
|
4
|
+
|
|
5
|
+
```md
|
|
6
|
+
::: package.module.Class
|
|
7
|
+
options:
|
|
8
|
+
heading_level: 2
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Docs: https://mkdocstrings.github.io/usage/
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
from mdit_py_plugins.utils import is_code_block
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from markdown_it import MarkdownIt
|
|
24
|
+
from markdown_it.rules_block import StateBlock
|
|
25
|
+
|
|
26
|
+
INJECTION_PATTERN = re.compile(r"^:::\s+\S")
|
|
27
|
+
MKDOCSTRINGS_INJECTION_PREFIX = "mkdocstrings_injection"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_line(state: StateBlock, line: int, base_indent: int) -> str:
|
|
31
|
+
return state.src[state.bMarks[line] + base_indent : state.eMarks[line]]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _mkdocstrings_injection(
|
|
35
|
+
state: StateBlock,
|
|
36
|
+
start_line: int,
|
|
37
|
+
end_line: int,
|
|
38
|
+
silent: bool,
|
|
39
|
+
) -> bool:
|
|
40
|
+
if is_code_block(state, start_line):
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
base_indent = state.tShift[start_line]
|
|
44
|
+
header = _get_line(state, start_line, base_indent)
|
|
45
|
+
if not INJECTION_PATTERN.match(header):
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
if silent:
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
lines = [header]
|
|
52
|
+
next_line = start_line + 1
|
|
53
|
+
while next_line < end_line:
|
|
54
|
+
if not state.isEmpty(next_line) and state.tShift[next_line] <= base_indent:
|
|
55
|
+
break
|
|
56
|
+
lines.append(_get_line(state, next_line, base_indent))
|
|
57
|
+
next_line += 1
|
|
58
|
+
|
|
59
|
+
while len(lines) > 1 and not lines[-1].strip():
|
|
60
|
+
lines.pop()
|
|
61
|
+
next_line -= 1
|
|
62
|
+
|
|
63
|
+
token = state.push(MKDOCSTRINGS_INJECTION_PREFIX, "", 0)
|
|
64
|
+
token.content = "\n".join(lines)
|
|
65
|
+
token.block = True
|
|
66
|
+
token.map = [start_line, next_line]
|
|
67
|
+
|
|
68
|
+
state.line = next_line
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def mkdocstrings_injection_plugin(md: MarkdownIt) -> None:
|
|
73
|
+
md.block.ruler.before(
|
|
74
|
+
"paragraph",
|
|
75
|
+
MKDOCSTRINGS_INJECTION_PREFIX,
|
|
76
|
+
_mkdocstrings_injection,
|
|
77
|
+
{"alt": ["paragraph"]},
|
|
78
|
+
)
|
|
@@ -17,7 +17,6 @@ from markdown_it import MarkdownIt
|
|
|
17
17
|
from mdformat_mkdocs._synced.admon_factories import new_token
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
|
-
from markdown_it import MarkdownIt
|
|
21
20
|
from markdown_it.rules_inline import StateInline
|
|
22
21
|
|
|
23
22
|
_ATTR_LIST_PATTERN = re.compile(r"{:? (?P<attrs>[^}]+) }")
|
|
@@ -38,20 +37,22 @@ def _python_markdown_attr_list(state: StateInline, silent: bool) -> bool:
|
|
|
38
37
|
if state.linkLevel > 0:
|
|
39
38
|
return False
|
|
40
39
|
|
|
41
|
-
# Look backwards for unclosed '['
|
|
42
|
-
|
|
43
|
-
text_before = state.src[search_start:state.pos]
|
|
40
|
+
# Look backwards for unclosed '[' — no length cap; any fixed cap fails for long URLs.
|
|
41
|
+
text_before = state.src[: state.pos]
|
|
44
42
|
open_brackets = text_before.count("[") - text_before.count("]")
|
|
45
43
|
if open_brackets > 0:
|
|
46
44
|
# We might be inside a link, check if there's '](' after our match
|
|
47
45
|
match_end_pos = state.pos + match.end()
|
|
48
46
|
if match_end_pos < len(state.src):
|
|
49
|
-
lookahead = state.src[
|
|
47
|
+
lookahead = state.src[
|
|
48
|
+
match_end_pos : min(match_end_pos + 100, len(state.src))
|
|
49
|
+
]
|
|
50
50
|
if "](" in lookahead:
|
|
51
51
|
# Very likely inside link text, don't match
|
|
52
52
|
return False
|
|
53
53
|
|
|
54
54
|
if silent:
|
|
55
|
+
state.pos += match.end() # skipToken only auto-advances when ok=False
|
|
55
56
|
return True
|
|
56
57
|
|
|
57
58
|
original_pos = state.pos
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Inline rule: parse ``[text](url with spaces)`` as a link.
|
|
2
|
+
|
|
3
|
+
CommonMark requires angle brackets for link destinations containing spaces.
|
|
4
|
+
Python-Markdown/MkDocs users write these without angle brackets. Without
|
|
5
|
+
this rule, markdown-it treats the whole construct as plain text, and
|
|
6
|
+
mdformat's HTML stability check fires.
|
|
7
|
+
|
|
8
|
+
This rule fires BEFORE markdown-it's built-in link rule. When it detects a
|
|
9
|
+
URL with at least one literal space (and no angle brackets), it emits proper
|
|
10
|
+
link tokens with the space percent-encoded in the href, matching the HTML
|
|
11
|
+
that mdformat outputs for the corrected form ``[text](<url with spaces>)``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
from markdown_it import MarkdownIt
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from markdown_it.rules_inline import StateInline
|
|
22
|
+
|
|
23
|
+
SPACED_URL_LINK_PREFIX = "spaced_url_link"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _spaced_url_link(state: StateInline, silent: bool) -> bool:
|
|
27
|
+
src = state.src
|
|
28
|
+
pos = state.pos
|
|
29
|
+
|
|
30
|
+
if src[pos] != "[":
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
bracket_end = src.find("]", pos + 1)
|
|
34
|
+
if bracket_end == -1 or bracket_end + 1 >= len(src) or src[bracket_end + 1] != "(":
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
paren_start = bracket_end + 2
|
|
38
|
+
paren_end = src.find(")", paren_start)
|
|
39
|
+
if paren_end == -1:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
url = src[paren_start:paren_end]
|
|
43
|
+
if " " not in url or url.startswith("<"):
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
first_space = url.index(" ")
|
|
47
|
+
char_after_space = url[first_space + 1] if first_space + 1 < len(url) else ""
|
|
48
|
+
if char_after_space in {'"', "'", "("}:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
if not silent:
|
|
52
|
+
old_pos_max = state.posMax
|
|
53
|
+
|
|
54
|
+
token = state.push("link_open", "a", 1)
|
|
55
|
+
token.attrs = {"href": url.replace(" ", "%20")}
|
|
56
|
+
token.markup = ""
|
|
57
|
+
token.info = ""
|
|
58
|
+
|
|
59
|
+
state.pos = pos + 1
|
|
60
|
+
state.posMax = bracket_end
|
|
61
|
+
state.linkLevel += 1
|
|
62
|
+
state.md.inline.tokenize(state)
|
|
63
|
+
state.linkLevel -= 1
|
|
64
|
+
state.posMax = old_pos_max
|
|
65
|
+
|
|
66
|
+
state.push("link_close", "a", -1)
|
|
67
|
+
|
|
68
|
+
state.pos = paren_end + 1
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def spaced_url_link_plugin(md: MarkdownIt) -> None:
|
|
73
|
+
"""Register the spaced-URL inline rule before the built-in link rule."""
|
|
74
|
+
md.inline.ruler.before(
|
|
75
|
+
"link",
|
|
76
|
+
SPACED_URL_LINK_PREFIX,
|
|
77
|
+
_spaced_url_link,
|
|
78
|
+
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
import textwrap
|
|
6
7
|
from functools import partial
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
@@ -10,7 +11,7 @@ from mdformat.renderer import DEFAULT_RENDERERS, RenderContext, RenderTreeNode
|
|
|
10
11
|
|
|
11
12
|
from ._helpers import ContextOptions, get_conf
|
|
12
13
|
from ._normalize_list import normalize_list as unbounded_normalize_list
|
|
13
|
-
from ._postprocess_inline import postprocess_list_wrap
|
|
14
|
+
from ._postprocess_inline import postprocess_list_wrap as _postprocess_list_wrap
|
|
14
15
|
from .mdit_plugins import (
|
|
15
16
|
AMSMATH_BLOCK,
|
|
16
17
|
DOLLARMATH_BLOCK,
|
|
@@ -19,6 +20,7 @@ from .mdit_plugins import (
|
|
|
19
20
|
MKDOCSTRINGS_AUTOREFS_PREFIX,
|
|
20
21
|
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
|
|
21
22
|
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX,
|
|
23
|
+
MKDOCSTRINGS_INJECTION_PREFIX,
|
|
22
24
|
PYMD_ABBREVIATIONS_PREFIX,
|
|
23
25
|
PYMD_CAPTIONS_PREFIX,
|
|
24
26
|
PYMD_SNIPPET_PREFIX,
|
|
@@ -30,6 +32,7 @@ from .mdit_plugins import (
|
|
|
30
32
|
material_deflist_plugin,
|
|
31
33
|
mkdocstrings_autorefs_plugin,
|
|
32
34
|
mkdocstrings_crossreference_plugin,
|
|
35
|
+
mkdocstrings_injection_plugin,
|
|
33
36
|
pymd_abbreviations_plugin,
|
|
34
37
|
pymd_admon_plugin,
|
|
35
38
|
pymd_arithmatex_plugin,
|
|
@@ -39,6 +42,7 @@ from .mdit_plugins import (
|
|
|
39
42
|
render_material_definition_body,
|
|
40
43
|
render_material_definition_list,
|
|
41
44
|
render_material_definition_term,
|
|
45
|
+
spaced_url_link_plugin,
|
|
42
46
|
)
|
|
43
47
|
|
|
44
48
|
if TYPE_CHECKING:
|
|
@@ -108,10 +112,12 @@ def update_mdit(mdit: MarkdownIt) -> None:
|
|
|
108
112
|
mdit.use(material_content_tabs_plugin)
|
|
109
113
|
mdit.use(material_deflist_plugin)
|
|
110
114
|
mdit.use(mkdocstrings_autorefs_plugin)
|
|
115
|
+
mdit.use(mkdocstrings_injection_plugin)
|
|
111
116
|
mdit.use(pymd_abbreviations_plugin)
|
|
112
117
|
mdit.use(pymd_admon_plugin)
|
|
113
118
|
mdit.use(pymd_snippet_plugin)
|
|
114
119
|
mdit.use(python_markdown_attr_list_plugin)
|
|
120
|
+
mdit.use(spaced_url_link_plugin)
|
|
115
121
|
|
|
116
122
|
if cli_is_ignore_missing_references(mdit.options):
|
|
117
123
|
mdit.use(mkdocstrings_crossreference_plugin)
|
|
@@ -134,29 +140,42 @@ def _render_math_inline(node: RenderTreeNode, context: RenderContext) -> str: #
|
|
|
134
140
|
return f"${content}$"
|
|
135
141
|
|
|
136
142
|
|
|
143
|
+
def _strip_blockquote_markers(content: str) -> str:
|
|
144
|
+
"""Strip blockquote markers from math block content.
|
|
145
|
+
|
|
146
|
+
markdown-it includes "> " prefixes when block math appears inside blockquotes.
|
|
147
|
+
"""
|
|
148
|
+
lines = content.split("\n")
|
|
149
|
+
return "\n".join(
|
|
150
|
+
line.removeprefix("> ") if line.startswith("> ") else line for line in lines
|
|
151
|
+
).strip()
|
|
152
|
+
|
|
153
|
+
|
|
137
154
|
def _render_math_block(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001
|
|
138
155
|
"""Render block math with original delimiters."""
|
|
139
156
|
markup = node.markup
|
|
140
|
-
|
|
157
|
+
cleaned_content = _strip_blockquote_markers(node.content)
|
|
158
|
+
|
|
141
159
|
if markup == "$$":
|
|
142
|
-
return f"$$\n{
|
|
160
|
+
return f"$$\n{cleaned_content}\n$$"
|
|
143
161
|
if markup == "\\[":
|
|
144
|
-
return f"\\[\n{
|
|
162
|
+
return f"\\[\n{cleaned_content}\n\\]"
|
|
145
163
|
# Fallback
|
|
146
|
-
return f"$$\n{
|
|
164
|
+
return f"$$\n{cleaned_content}\n$$"
|
|
147
165
|
|
|
148
166
|
|
|
149
167
|
def _render_math_block_eqno(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001
|
|
150
168
|
"""Render block math with equation label."""
|
|
151
169
|
markup = node.markup
|
|
152
|
-
|
|
153
|
-
|
|
170
|
+
label = node.info
|
|
171
|
+
cleaned_content = _strip_blockquote_markers(node.content)
|
|
172
|
+
|
|
154
173
|
if markup == "$$":
|
|
155
|
-
return f"$$\n{
|
|
174
|
+
return f"$$\n{cleaned_content}\n$$ ({label})"
|
|
156
175
|
if markup == "\\[":
|
|
157
|
-
return f"\\[\n{
|
|
176
|
+
return f"\\[\n{cleaned_content}\n\\] ({label})"
|
|
158
177
|
# Fallback
|
|
159
|
-
return f"$$\n{
|
|
178
|
+
return f"$$\n{cleaned_content}\n$$ ({label})"
|
|
160
179
|
|
|
161
180
|
|
|
162
181
|
def _render_amsmath(node: RenderTreeNode, context: RenderContext) -> str: # noqa: ARG001
|
|
@@ -176,40 +195,82 @@ def _render_inline_content(node: RenderTreeNode, context: RenderContext) -> str:
|
|
|
176
195
|
return inline.content
|
|
177
196
|
|
|
178
197
|
|
|
179
|
-
|
|
180
|
-
|
|
198
|
+
_ESCAPED_LINK_SPACED_URL = re.compile(r"\\\[([^\]]*)\\\]\(([^)]*[ ][^)]*)\)")
|
|
199
|
+
_PERCENT_ENCODED_URL_LINK = re.compile(r"\[([^\]]*)\]\(([^)]*%20[^)]*)\)")
|
|
200
|
+
_ANGLE_BRACKET_SPACED_URL_SOURCE = re.compile(r"\]\(<([^>]* [^>]*)>\)")
|
|
201
|
+
_UNBRACKETED_SPACED_URL_SOURCE = re.compile(r"\]\(([^<>()]*\s[^<>()]*)\)")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _fix_links_with_spaced_urls(
|
|
205
|
+
text: str,
|
|
206
|
+
node: RenderTreeNode,
|
|
207
|
+
context: RenderContext, # noqa: ARG001
|
|
208
|
+
) -> str:
|
|
209
|
+
r"""Rewrite links with space-containing URLs to angle-bracket syntax.
|
|
210
|
+
|
|
211
|
+
CommonMark requires link destinations with spaces to use angle brackets.
|
|
212
|
+
Handles three cases:
|
|
213
|
+
|
|
214
|
+
1. markdown-it fails to parse [text](url space) as a link and mdformat
|
|
215
|
+
escapes the brackets — detects ``\\[text\\](url space)`` and rewrites to
|
|
216
|
+
``[text](<url space>)``. (Fallback for edge cases not caught by the
|
|
217
|
+
spaced_url_link mdit rule.)
|
|
218
|
+
2. markdown-it parses [text](<url space>) correctly but percent-encodes
|
|
219
|
+
the space in the href — detects [text](url%20space) and restores to
|
|
220
|
+
[text](<url space>) using the original source in node.content.
|
|
221
|
+
3. The spaced_url_link mdit rule parses [text](url space) as a link with
|
|
222
|
+
percent-encoded href — same rewrite as case 2, but source has no
|
|
223
|
+
angle brackets so a separate pattern extracts the original URL.
|
|
224
|
+
|
|
225
|
+
Addresses: https://github.com/KyleKing/mdformat-mkdocs/issues/80
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
text = _ESCAPED_LINK_SPACED_URL.sub(
|
|
229
|
+
lambda m: f"[{m.group(1)}](<{m.group(2)}>)",
|
|
230
|
+
text,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
spaced_urls_from_source = {
|
|
234
|
+
m.group(1) for m in _ANGLE_BRACKET_SPACED_URL_SOURCE.finditer(node.content)
|
|
235
|
+
} | {m.group(1) for m in _UNBRACKETED_SPACED_URL_SOURCE.finditer(node.content)}
|
|
236
|
+
if spaced_urls_from_source:
|
|
181
237
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
238
|
+
def _restore_spaced_url(m: re.Match[str]) -> str:
|
|
239
|
+
link_text, encoded_url = m.group(1), m.group(2)
|
|
240
|
+
decoded_url = encoded_url.replace("%20", " ")
|
|
241
|
+
if decoded_url in spaced_urls_from_source:
|
|
242
|
+
return f"[{link_text}](<{decoded_url}>)"
|
|
243
|
+
return m.group(0)
|
|
186
244
|
|
|
187
|
-
|
|
245
|
+
text = _PERCENT_ENCODED_URL_LINK.sub(_restore_spaced_url, text)
|
|
246
|
+
|
|
247
|
+
return text
|
|
188
248
|
|
|
189
|
-
This could break at any time, so this is a best effort to resolve issues like:
|
|
190
|
-
https://github.com/KyleKing/mdformat-mkdocs/issues/34#issuecomment-3589835341
|
|
191
249
|
|
|
250
|
+
def _render_text(node: RenderTreeNode, context: RenderContext) -> str:
|
|
251
|
+
r"""Re-escape dollar signs that mdformat core stripped.
|
|
252
|
+
|
|
253
|
+
mdformat removes "unnecessary" backslash escapes (\$ -> $), but with math enabled
|
|
254
|
+
those bare $ become math delimiters. Compares text content against the parent
|
|
255
|
+
inline token (which preserves backslashes) to detect and restore the escapes.
|
|
256
|
+
|
|
257
|
+
Related: https://github.com/KyleKing/mdformat-mkdocs/issues/77
|
|
192
258
|
"""
|
|
193
|
-
default_renderer = DEFAULT_RENDERERS.get("
|
|
259
|
+
default_renderer = DEFAULT_RENDERERS.get("text")
|
|
194
260
|
if default_renderer is None:
|
|
195
261
|
return node.content
|
|
196
262
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
# Only process single-backtick code (not double-backtick code with embedded backticks)
|
|
200
|
-
if not (result.startswith("`") and result.endswith("`") and "``" not in result):
|
|
201
|
-
return result
|
|
263
|
+
text = default_renderer(node, context)
|
|
202
264
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
has_trailing_space = content.endswith(" ")
|
|
265
|
+
if cli_is_no_mkdocs_math(context.options):
|
|
266
|
+
return text
|
|
206
267
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
268
|
+
if node.parent and node.parent.type == "inline":
|
|
269
|
+
parent_content = node.parent.content
|
|
270
|
+
if "$" in text and r"\$" in parent_content:
|
|
271
|
+
text = re.sub(r"(?<!\\)\$", r"\$", text)
|
|
211
272
|
|
|
212
|
-
return
|
|
273
|
+
return text
|
|
213
274
|
|
|
214
275
|
|
|
215
276
|
def _render_heading_autoref(node: RenderTreeNode, context: RenderContext) -> str:
|
|
@@ -307,12 +368,12 @@ RENDERERS: Mapping[str, Render] = {
|
|
|
307
368
|
"admonition_title": render_admon_title,
|
|
308
369
|
"admonition_mkdocs": add_extra_admon_newline,
|
|
309
370
|
"admonition_mkdocs_title": render_admon_title,
|
|
310
|
-
"code_inline": _render_code_inline,
|
|
311
371
|
"content_tab_mkdocs": add_extra_admon_newline,
|
|
312
372
|
"content_tab_mkdocs_title": render_admon_title,
|
|
373
|
+
"dd": render_material_definition_body,
|
|
313
374
|
"dl": render_material_definition_list,
|
|
314
375
|
"dt": render_material_definition_term,
|
|
315
|
-
"
|
|
376
|
+
"text": _render_text,
|
|
316
377
|
# Math support (from mdit-py-plugins)
|
|
317
378
|
DOLLARMATH_INLINE: _render_math_inline,
|
|
318
379
|
DOLLARMATH_BLOCK: _render_math_block,
|
|
@@ -323,6 +384,7 @@ RENDERERS: Mapping[str, Render] = {
|
|
|
323
384
|
PYMD_CAPTIONS_PREFIX: render_pymd_caption,
|
|
324
385
|
MKDOCSTRINGS_AUTOREFS_PREFIX: _render_meta_content,
|
|
325
386
|
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
|
|
387
|
+
MKDOCSTRINGS_INJECTION_PREFIX: _render_node_content,
|
|
326
388
|
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX: _render_heading_autoref,
|
|
327
389
|
PYMD_ABBREVIATIONS_PREFIX: _render_inline_content,
|
|
328
390
|
PYMD_SNIPPET_PREFIX: _render_inline_content,
|
|
@@ -330,10 +392,24 @@ RENDERERS: Mapping[str, Render] = {
|
|
|
330
392
|
}
|
|
331
393
|
|
|
332
394
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
395
|
+
if TYPE_CHECKING:
|
|
396
|
+
normalize_list: Postprocess
|
|
397
|
+
postprocess_inline: Postprocess
|
|
398
|
+
else:
|
|
399
|
+
normalize_list = partial(
|
|
400
|
+
unbounded_normalize_list,
|
|
401
|
+
check_if_align_semantic_breaks_in_lists=cli_is_align_semantic_breaks_in_lists,
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def postprocess_inline(
|
|
405
|
+
text: str,
|
|
406
|
+
node: RenderTreeNode,
|
|
407
|
+
context: RenderContext,
|
|
408
|
+
) -> str:
|
|
409
|
+
"""Run all inline postprocessors in sequence."""
|
|
410
|
+
text = _postprocess_list_wrap(text, node, context)
|
|
411
|
+
return _fix_links_with_spaced_urls(text, node, context)
|
|
412
|
+
|
|
337
413
|
|
|
338
414
|
# A mapping from `RenderTreeNode.type` to a `Postprocess` that does
|
|
339
415
|
# postprocessing for the output of the `Render` function. Unlike
|
|
@@ -342,7 +418,7 @@ normalize_list = partial(
|
|
|
342
418
|
# will run in series.
|
|
343
419
|
POSTPROCESSORS: Mapping[str, Postprocess] = {
|
|
344
420
|
"bullet_list": normalize_list,
|
|
345
|
-
"inline":
|
|
421
|
+
"inline": postprocess_inline,
|
|
346
422
|
"ordered_list": normalize_list,
|
|
347
423
|
"paragraph": escape_deflist,
|
|
348
424
|
}
|
|
@@ -25,7 +25,7 @@ license-files = ["LICENSE"]
|
|
|
25
25
|
name = "mdformat_mkdocs"
|
|
26
26
|
readme = "README.md"
|
|
27
27
|
requires-python = ">=3.10.0"
|
|
28
|
-
version = "5.
|
|
28
|
+
version = "5.2.0"
|
|
29
29
|
|
|
30
30
|
[project.entry-points."mdformat.parser_extension"]
|
|
31
31
|
mkdocs = "mdformat_mkdocs"
|
|
@@ -56,6 +56,7 @@ recommended-mdsf = [
|
|
|
56
56
|
]
|
|
57
57
|
test = [
|
|
58
58
|
"beartype >= 0.21.0",
|
|
59
|
+
"hypothesis >= 6.100.0",
|
|
59
60
|
"pytest >= 9.0.1",
|
|
60
61
|
"pytest-beartype >= 0.2.0",
|
|
61
62
|
"pytest-cov >= 7.0.0",
|
|
@@ -69,7 +70,7 @@ homepage = "https://github.com/kyleking/mdformat-mkdocs"
|
|
|
69
70
|
|
|
70
71
|
[tool.commitizen]
|
|
71
72
|
tag_format = "v${version}"
|
|
72
|
-
version = "5.
|
|
73
|
+
version = "5.2.0"
|
|
73
74
|
version_files = ["mdformat_mkdocs/__init__.py", "pyproject.toml:^version"]
|
|
74
75
|
|
|
75
76
|
[tool.mypy]
|
|
@@ -183,6 +184,13 @@ isolated_build = true
|
|
|
183
184
|
requires = ["tox>=4.32.0"]
|
|
184
185
|
skip_missing_interpreters = false
|
|
185
186
|
|
|
187
|
+
[tool.tox.env.canary]
|
|
188
|
+
basepython = ["py314"]
|
|
189
|
+
changedir = "{tox_root}"
|
|
190
|
+
commands = [["python", "scripts/canary.py", {default = [], extend = true, replace = "posargs"}]]
|
|
191
|
+
description = "Run mdformat --check against real downstream repos. Optionally specify: '-- ruff uv' to test a subset."
|
|
192
|
+
skip_install = false
|
|
193
|
+
|
|
186
194
|
[tool.tox.env.cz]
|
|
187
195
|
basepython = ["py314"]
|
|
188
196
|
commands = [["cz", "bump", {default = [], extend = true, replace = "posargs"}]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/admon_factories/README.md
RENAMED
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/_synced/admon_factories/__init__.py
RENAMED
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_material_admon.py
RENAMED
|
File without changes
|
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_abbreviations.py
RENAMED
|
File without changes
|
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_arithmatex.py
RENAMED
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_captions.py
RENAMED
|
File without changes
|
{mdformat_mkdocs-5.1.4 → mdformat_mkdocs-5.2.0}/mdformat_mkdocs/mdit_plugins/_pymd_snippet.py
RENAMED
|
File without changes
|
|
File without changes
|