Skip to content

Commit

Permalink
Merge pull request #43 from KyleKing/fix-34
Browse files Browse the repository at this point in the history
fix(#34): support pymdown snippets
  • Loading branch information
KyleKing authored Dec 17, 2024
2 parents ce67108 + 157750e commit 305eaa7
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 3 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ Supports:
- *Note*: when specifying `--align-semantic-breaks-in-lists`, the nested indent for ordered lists is three, but is otherwise a multiple of four
- Unordered list bullets are converted to dashes (`-`) instead of `*`
- 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)
- [MkDocs-Material Admonitions](https://squidfunk.github.io/mkdocs-material/reference/admonitions)
- [MkDocs-Material Admonitions\*](https://squidfunk.github.io/mkdocs-material/reference/admonitions)
- \*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))
- [MkDocs-Material Content Tabs\*](https://squidfunk.github.io/mkdocs-material/reference/content-tabs)
- \*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.
- [mkdocstrings Anchors (autorefs)](https://mkdocstrings.github.io/autorefs/#markdown-anchors)
- [mkdocstrings Cross-References](https://mkdocstrings.github.io/usage/#cross-references)
- [Python Markdown "Abbreviations"\*](https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-abbreviations)
- \*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
- [Python Markdown "Snippets"\*](https://facelessuser.github.io/pymdown-extensions/extensions/snippets)
- \*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

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)

Expand Down
3 changes: 3 additions & 0 deletions mdformat_mkdocs/mdit_plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
mkdocstrings_crossreference_plugin,
)
from ._pymd_abbreviations import PYMD_ABBREVIATIONS_PREFIX, pymd_abbreviations_plugin
from ._pymd_snippet import PYMD_SNIPPET_PREFIX, pymd_snippet_plugin
from ._python_markdown_admon import python_markdown_admon_plugin

__all__ = (
Expand All @@ -24,10 +25,12 @@
"MKDOCSTRINGS_CROSSREFERENCE_PREFIX",
"MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX",
"PYMD_ABBREVIATIONS_PREFIX",
"PYMD_SNIPPET_PREFIX",
"material_admon_plugin",
"material_content_tabs_plugin",
"mkdocstrings_autorefs_plugin",
"mkdocstrings_crossreference_plugin",
"pymd_abbreviations_plugin",
"pymd_snippet_plugin",
"python_markdown_admon_plugin",
)
100 changes: 100 additions & 0 deletions mdformat_mkdocs/mdit_plugins/_pymd_snippet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""Python-Markdown Snippets.
WARNING: matches only the "scissors" portion, leaving the rest unparsed
```md
--8<-- ...
```
Docs: <https://facelessuser.github.io/pymd-extensions/extensions/snippets>
"""

from __future__ import annotations

import re

from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block

from mdformat_mkdocs._synced.admon_factories import new_token

_SNIPPET_MARKER = "--8<--"
_ABBREVIATION_PATTERN = re.compile(rf"^{_SNIPPET_MARKER}(?P<label>.*)")

PYMD_SNIPPET_PREFIX = "pymd_snippet"


def _content(state: StateBlock, start_line: int) -> str:
"""Content."""
start = state.bMarks[start_line] + state.tShift[start_line]
maximum = state.eMarks[start_line]
return state.src[start:maximum]


def _parse(
state: StateBlock,
match: re.Match[str],
start_line: int,
end_line: int,
) -> tuple[int, str]:
"""Return the max line and matched content."""
max_line = start_line + 1
inline = f"{_SNIPPET_MARKER}{match['label']}"

if not match["label"]:
max_search = 20 # Upper limit of lines to search for multi-line snippet
current_line = start_line + 1
inner: list[str] = []
while (current_line - start_line) < max_search:
if max_line == end_line:
break # no 'label'
line = _content(state, current_line)
if _ABBREVIATION_PATTERN.match(line):
max_line = current_line + 1
inline = "\n".join([_SNIPPET_MARKER, *inner, _SNIPPET_MARKER])
break
if line:
inner.append(line)
current_line += 1

return max_line, inline


def _pymd_snippet(
state: StateBlock,
start_line: int,
end_line: int,
silent: bool,
) -> bool:
if is_code_block(state, start_line):
return False

match = _ABBREVIATION_PATTERN.match(_content(state, start_line))
if match is None:
return False

if silent:
return True

max_line, inline = _parse(state, match, start_line, end_line)

with new_token(state, PYMD_SNIPPET_PREFIX, "p"):
tkn_inline = state.push("inline", "", 0)
tkn_inline.content = inline
tkn_inline.map = [start_line, max_line]
tkn_inline.children = []

state.line = max_line

return True


def pymd_snippet_plugin(md: MarkdownIt) -> None:
md.block.ruler.before(
"reference",
PYMD_SNIPPET_PREFIX,
_pymd_snippet,
{"alt": ["paragraph"]},
)
6 changes: 5 additions & 1 deletion mdformat_mkdocs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
MKDOCSTRINGS_CROSSREFERENCE_PREFIX,
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX,
PYMD_ABBREVIATIONS_PREFIX,
PYMD_SNIPPET_PREFIX,
material_admon_plugin,
material_content_tabs_plugin,
mkdocstrings_autorefs_plugin,
mkdocstrings_crossreference_plugin,
pymd_abbreviations_plugin,
pymd_snippet_plugin,
python_markdown_admon_plugin,
)

Expand Down Expand Up @@ -69,7 +71,8 @@ def update_mdit(mdit: MarkdownIt) -> None:
mdit.use(material_content_tabs_plugin)
mdit.use(mkdocstrings_autorefs_plugin)
mdit.use(pymd_abbreviations_plugin)
mdit.use(python_markdown_admon_plugin)
mdit.use(pymd_snippet_plugin)
mdit.use(python_markdown_admon_plugin) # FIXME: standardize pymd name

if cli_is_ignore_missing_references(mdit.options):
mdit.use(mkdocstrings_crossreference_plugin)
Expand Down Expand Up @@ -179,6 +182,7 @@ def add_extra_admon_newline(node: RenderTreeNode, context: RenderContext) -> str
MKDOCSTRINGS_HEADING_AUTOREFS_PREFIX: _render_heading_autoref,
MKDOCSTRINGS_CROSSREFERENCE_PREFIX: _render_cross_reference,
PYMD_ABBREVIATIONS_PREFIX: _render_inline_content,
PYMD_SNIPPET_PREFIX: _render_inline_content,
}


Expand Down
33 changes: 33 additions & 0 deletions tests/format/fixtures/pymd_snippet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pymdown snippets (https://github.com/KyleKing/mdformat-mkdocs/issues/34)
.
# Snippets

--8<-- "filename.ext"

--8<-- "; skip.md"


--8<--
filename.md
filename.log
--8<--

Content of file A.

Content of file B.
.
# Snippets

--8<-- "filename.ext"

--8<-- "; skip.md"

--8<--
filename.md
filename.log
--8<--

Content of file A.

Content of file B.
.
1 change: 1 addition & 0 deletions tests/format/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def flatten(nested_list: list[list[T]]) -> list[T]:
"material_content_tabs.md",
"mkdocstrings_autorefs.md",
"pymd_abbreviations.md",
"pymd_snippet.md",
"text.md",
)
],
Expand Down
28 changes: 28 additions & 0 deletions tests/render/fixtures/pymd_snippet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
pymdown snippets (https://github.com/KyleKing/mdformat-mkdocs/issues/34)
.
# Snippets

--8<-- "filename.ext"

--8<-- "; skip.md"


--8<--
filename.md
filename.log
--8<--

Content of file A.

Content of file B.
.
<h1>Snippets</h1>
<p>--8&lt;-- &quot;filename.ext&quot;</p>
<p>--8&lt;-- &quot;; skip.md&quot;</p>
<p>--8&lt;--
filename.md
filename.log
--8&lt;--</p>
<p>Content of file A.</p>
<p>Content of file B.</p>
.
7 changes: 6 additions & 1 deletion tests/render/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
mkdocstrings_autorefs_plugin,
mkdocstrings_crossreference_plugin,
pymd_abbreviations_plugin,
pymd_snippet_plugin,
)
from tests.helpers import print_text

Expand All @@ -23,7 +24,7 @@ def with_plugin(filename, plugins):
@pytest.mark.parametrize(
("line", "title", "text", "expected", "plugins"),
[
*with_plugin("matieral_admonitions.md", [material_admon_plugin]),
*with_plugin("material_admonitions.md", [material_admon_plugin]),
*with_plugin(
"material_content_tabs.md",
[material_admon_plugin, material_content_tabs_plugin],
Expand All @@ -34,6 +35,10 @@ def with_plugin(filename, plugins):
"mkdocstrings_crossreference.md",
[mkdocstrings_crossreference_plugin],
),
*with_plugin(
"pymd_snippet.md",
[pymd_snippet_plugin],
),
],
)
def test_render(line, title, text, expected, plugins):
Expand Down

0 comments on commit 305eaa7

Please sign in to comment.