Skip to content

Commit 305eaa7

Browse files
authored
Merge pull request #43 from KyleKing/fix-34
fix(#34): support pymdown snippets
2 parents ce67108 + 157750e commit 305eaa7

File tree

9 files changed

+180
-3
lines changed

9 files changed

+180
-3
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ Supports:
1515
- *Note*: when specifying `--align-semantic-breaks-in-lists`, the nested indent for ordered lists is three, but is otherwise a multiple of four
1616
- Unordered list bullets are converted to dashes (`-`) instead of `*`
1717
- By default, ordered lists are standardized on a single digit (`1.` or `0.`) unless `--number` is specified, then `mdformat-mkdocs` will apply consecutive numbering to ordered lists [for consistency with `mdformat`](https://github.com/executablebooks/mdformat?tab=readme-ov-file#options)
18-
- [MkDocs-Material Admonitions](https://squidfunk.github.io/mkdocs-material/reference/admonitions)
18+
- [MkDocs-Material Admonitions\*](https://squidfunk.github.io/mkdocs-material/reference/admonitions)
19+
- \*Note: `mdformat-admon` will format the same admonitions, but for consistency with the mkdocs styleguide, an extra space will be added by this package ([#22](https://github.com/KyleKing/mdformat-admon/pull/22))
1920
- [MkDocs-Material Content Tabs\*](https://squidfunk.github.io/mkdocs-material/reference/content-tabs)
2021
- \*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.
2122
- [mkdocstrings Anchors (autorefs)](https://mkdocstrings.github.io/autorefs/#markdown-anchors)
2223
- [mkdocstrings Cross-References](https://mkdocstrings.github.io/usage/#cross-references)
2324
- [Python Markdown "Abbreviations"\*](https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-abbreviations)
2425
- \*Note: the markup (HTML) rendered for abbreviations is not useful for rendering. If important, I'm open to contributions because the implementation could be challenging
26+
- [Python Markdown "Snippets"\*](https://facelessuser.github.io/pymdown-extensions/extensions/snippets)
27+
- \*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
2528

2629
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)
2730

mdformat_mkdocs/mdit_plugins/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
mkdocstrings_crossreference_plugin,
1616
)
1717
from ._pymd_abbreviations import PYMD_ABBREVIATIONS_PREFIX, pymd_abbreviations_plugin
18+
from ._pymd_snippet import PYMD_SNIPPET_PREFIX, pymd_snippet_plugin
1819
from ._python_markdown_admon import python_markdown_admon_plugin
1920

2021
__all__ = (
@@ -24,10 +25,12 @@
2425
"MKDOCSTRINGS_CROSSREFERENCE_PREFIX",
2526
"MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX",
2627
"PYMD_ABBREVIATIONS_PREFIX",
28+
"PYMD_SNIPPET_PREFIX",
2729
"material_admon_plugin",
2830
"material_content_tabs_plugin",
2931
"mkdocstrings_autorefs_plugin",
3032
"mkdocstrings_crossreference_plugin",
3133
"pymd_abbreviations_plugin",
34+
"pymd_snippet_plugin",
3235
"python_markdown_admon_plugin",
3336
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Python-Markdown Snippets.
2+
3+
WARNING: matches only the "scissors" portion, leaving the rest unparsed
4+
5+
```md
6+
--8<-- ...
7+
```
8+
9+
Docs: <https://facelessuser.github.io/pymd-extensions/extensions/snippets>
10+
11+
"""
12+
13+
from __future__ import annotations
14+
15+
import re
16+
17+
from markdown_it import MarkdownIt
18+
from markdown_it.rules_block import StateBlock
19+
from mdit_py_plugins.utils import is_code_block
20+
21+
from mdformat_mkdocs._synced.admon_factories import new_token
22+
23+
_SNIPPET_MARKER = "--8<--"
24+
_ABBREVIATION_PATTERN = re.compile(rf"^{_SNIPPET_MARKER}(?P<label>.*)")
25+
26+
PYMD_SNIPPET_PREFIX = "pymd_snippet"
27+
28+
29+
def _content(state: StateBlock, start_line: int) -> str:
30+
"""Content."""
31+
start = state.bMarks[start_line] + state.tShift[start_line]
32+
maximum = state.eMarks[start_line]
33+
return state.src[start:maximum]
34+
35+
36+
def _parse(
37+
state: StateBlock,
38+
match: re.Match[str],
39+
start_line: int,
40+
end_line: int,
41+
) -> tuple[int, str]:
42+
"""Return the max line and matched content."""
43+
max_line = start_line + 1
44+
inline = f"{_SNIPPET_MARKER}{match['label']}"
45+
46+
if not match["label"]:
47+
max_search = 20 # Upper limit of lines to search for multi-line snippet
48+
current_line = start_line + 1
49+
inner: list[str] = []
50+
while (current_line - start_line) < max_search:
51+
if max_line == end_line:
52+
break # no 'label'
53+
line = _content(state, current_line)
54+
if _ABBREVIATION_PATTERN.match(line):
55+
max_line = current_line + 1
56+
inline = "\n".join([_SNIPPET_MARKER, *inner, _SNIPPET_MARKER])
57+
break
58+
if line:
59+
inner.append(line)
60+
current_line += 1
61+
62+
return max_line, inline
63+
64+
65+
def _pymd_snippet(
66+
state: StateBlock,
67+
start_line: int,
68+
end_line: int,
69+
silent: bool,
70+
) -> bool:
71+
if is_code_block(state, start_line):
72+
return False
73+
74+
match = _ABBREVIATION_PATTERN.match(_content(state, start_line))
75+
if match is None:
76+
return False
77+
78+
if silent:
79+
return True
80+
81+
max_line, inline = _parse(state, match, start_line, end_line)
82+
83+
with new_token(state, PYMD_SNIPPET_PREFIX, "p"):
84+
tkn_inline = state.push("inline", "", 0)
85+
tkn_inline.content = inline
86+
tkn_inline.map = [start_line, max_line]
87+
tkn_inline.children = []
88+
89+
state.line = max_line
90+
91+
return True
92+
93+
94+
def pymd_snippet_plugin(md: MarkdownIt) -> None:
95+
md.block.ruler.before(
96+
"reference",
97+
PYMD_SNIPPET_PREFIX,
98+
_pymd_snippet,
99+
{"alt": ["paragraph"]},
100+
)

mdformat_mkdocs/plugin.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
2020
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX,
2121
PYMD_ABBREVIATIONS_PREFIX,
22+
PYMD_SNIPPET_PREFIX,
2223
material_admon_plugin,
2324
material_content_tabs_plugin,
2425
mkdocstrings_autorefs_plugin,
2526
mkdocstrings_crossreference_plugin,
2627
pymd_abbreviations_plugin,
28+
pymd_snippet_plugin,
2729
python_markdown_admon_plugin,
2830
)
2931

@@ -69,7 +71,8 @@ def update_mdit(mdit: MarkdownIt) -> None:
6971
mdit.use(material_content_tabs_plugin)
7072
mdit.use(mkdocstrings_autorefs_plugin)
7173
mdit.use(pymd_abbreviations_plugin)
72-
mdit.use(python_markdown_admon_plugin)
74+
mdit.use(pymd_snippet_plugin)
75+
mdit.use(python_markdown_admon_plugin) # FIXME: standardize pymd name
7376

7477
if cli_is_ignore_missing_references(mdit.options):
7578
mdit.use(mkdocstrings_crossreference_plugin)
@@ -179,6 +182,7 @@ def add_extra_admon_newline(node: RenderTreeNode, context: RenderContext) -> str
179182
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX: _render_heading_autoref,
180183
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
181184
PYMD_ABBREVIATIONS_PREFIX: _render_inline_content,
185+
PYMD_SNIPPET_PREFIX: _render_inline_content,
182186
}
183187

184188

tests/format/fixtures/pymd_snippet.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
pymdown snippets (https://github.com/KyleKing/mdformat-mkdocs/issues/34)
2+
.
3+
# Snippets
4+
5+
--8<-- "filename.ext"
6+
7+
--8<-- "; skip.md"
8+
9+
10+
--8<--
11+
filename.md
12+
filename.log
13+
--8<--
14+
15+
Content of file A.
16+
17+
Content of file B.
18+
.
19+
# Snippets
20+
21+
--8<-- "filename.ext"
22+
23+
--8<-- "; skip.md"
24+
25+
--8<--
26+
filename.md
27+
filename.log
28+
--8<--
29+
30+
Content of file A.
31+
32+
Content of file B.
33+
.

tests/format/test_format.py

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def flatten(nested_list: list[list[T]]) -> list[T]:
2424
"material_content_tabs.md",
2525
"mkdocstrings_autorefs.md",
2626
"pymd_abbreviations.md",
27+
"pymd_snippet.md",
2728
"text.md",
2829
)
2930
],

tests/render/fixtures/pymd_snippet.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
pymdown snippets (https://github.com/KyleKing/mdformat-mkdocs/issues/34)
2+
.
3+
# Snippets
4+
5+
--8<-- "filename.ext"
6+
7+
--8<-- "; skip.md"
8+
9+
10+
--8<--
11+
filename.md
12+
filename.log
13+
--8<--
14+
15+
Content of file A.
16+
17+
Content of file B.
18+
.
19+
<h1>Snippets</h1>
20+
<p>--8&lt;-- &quot;filename.ext&quot;</p>
21+
<p>--8&lt;-- &quot;; skip.md&quot;</p>
22+
<p>--8&lt;--
23+
filename.md
24+
filename.log
25+
--8&lt;--</p>
26+
<p>Content of file A.</p>
27+
<p>Content of file B.</p>
28+
.

tests/render/test_render.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
mkdocstrings_autorefs_plugin,
1111
mkdocstrings_crossreference_plugin,
1212
pymd_abbreviations_plugin,
13+
pymd_snippet_plugin,
1314
)
1415
from tests.helpers import print_text
1516

@@ -23,7 +24,7 @@ def with_plugin(filename, plugins):
2324
@pytest.mark.parametrize(
2425
("line", "title", "text", "expected", "plugins"),
2526
[
26-
*with_plugin("matieral_admonitions.md", [material_admon_plugin]),
27+
*with_plugin("material_admonitions.md", [material_admon_plugin]),
2728
*with_plugin(
2829
"material_content_tabs.md",
2930
[material_admon_plugin, material_content_tabs_plugin],
@@ -34,6 +35,10 @@ def with_plugin(filename, plugins):
3435
"mkdocstrings_crossreference.md",
3536
[mkdocstrings_crossreference_plugin],
3637
),
38+
*with_plugin(
39+
"pymd_snippet.md",
40+
[pymd_snippet_plugin],
41+
),
3742
],
3843
)
3944
def test_render(line, title, text, expected, plugins):

0 commit comments

Comments
 (0)