diff --git a/mdformat_myst/plugin.py b/mdformat_myst/plugin.py index 13d796e..dca6d9b 100644 --- a/mdformat_myst/plugin.py +++ b/mdformat_myst/plugin.py @@ -8,11 +8,70 @@ from mdit_py_plugins.dollarmath import dollarmath_plugin from mdit_py_plugins.myst_blocks import myst_block_plugin from mdit_py_plugins.myst_role import myst_role_plugin +from mdit_py_plugins.container import container_plugin from mdformat_myst._directives import fence, render_fence_html _TARGET_PATTERN = re.compile(r"^\s*\(.+\)=\s*$") _ROLE_NAME_PATTERN = re.compile(r"({[a-zA-Z0-9_\-+:]+})") +_YAML_HEADER_PATTERN = re.compile(r"(?m)(^:\w+: .*$\n?)+|^---$\n(?s:.).*\n---\n") + +container_names = [ + "admonition", + "attention", + "caution", + "danger", + "div", + "dropdown", + "embed", + "error", + "exercise", + "exercise-end", + "exercise-start", + "figure", + "glossary", + "grid", + "grid-item", + "grid-item-card", + "hint", + "image", + "important", + "include", + "index", + "literal-include", + "margin", + "math", + "note", + "prf:algorithm", + "prf:assumption", + "prf:axiom", + "prf:conjecture", + "prf:corollary", + "prf:criterion", + "prf:definition", + "prf:example", + "prf:lemma", + "prf:observation", + "prf:proof", + "prf:property", + "prf:proposition", + "prf:remark", + "prf:theorem", + "seealso", + "show-index", + "sidebar", + "solution", + "solution-end", + "solution-start", + "span", + "tab-item", + "tab-set", + "table", + "tip", + "todo", + "topics", + "warning", +] def update_mdit(mdit: MarkdownIt) -> None: @@ -51,6 +110,36 @@ def update_mdit(mdit: MarkdownIt) -> None: mdit.add_render_rule("fence", render_fence_html) mdit.add_render_rule("code_block", render_fence_html) + for name in container_names: + container_plugin(mdit, name="{" + name + "}", marker=":") + + +def container_renderer( + node: RenderTreeNode, context: RenderContext, *args, **kwargs +) -> str: + children = node.children + paragraphs = [] + if children: + # Look at the tokens forming the first paragraph and see if + # they form a YAML header. This could be stricter: there + # should be exactly three tokens: paragraph open, YAML + # header, paragraph end. + tokens = children[0].to_tokens() + if all( + token.type in {'paragraph_open', 'paragraph_close'} or + _YAML_HEADER_PATTERN.fullmatch(token.content) + for token in tokens + ): + paragraphs.append('\n'.join(token.content.strip() + for token in tokens + if token.content)) + # and skip that first paragraph + children = children[1:] + + paragraphs.extend(child.render(context) for child in children) + + return node.markup + node.info + "\n" + "\n\n".join(paragraphs) + "\n" + node.markup + def _role_renderer(node: RenderTreeNode, context: RenderContext) -> str: role_name = "{" + node.meta["name"] + "}" @@ -130,4 +219,9 @@ def _escape_text(text: str, node: RenderTreeNode, context: RenderContext) -> str "math_block": _math_block_renderer, "fence": fence, } + + +for name in container_names: + RENDERERS["container_{" + name + "}"] = container_renderer + POSTPROCESSORS = {"paragraph": _escape_paragraph, "text": _escape_text} diff --git a/tests/data/fixtures.md b/tests/data/fixtures.md index 5a62b17..77a34b3 100644 --- a/tests/data/fixtures.md +++ b/tests/data/fixtures.md @@ -406,3 +406,94 @@ MyST directive, no opts or content ```{some-directive} args ``` . + +:::{admonition} MyST colon fenced directive with a title +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +. + +:::{admonition} MyST colon fenced directive with simple metadata +:class: foo +:truc: bla + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +::::{admonition} MyST colon fenced directive with nested directive with simple metadata +:::{image} foo.png +:class: foo +:truc: bla +::: +:::: + +% Admonitions with arbitrary yaml metadata are not yet supported. +% Issue: in a container, the `---` is interpreted as hrule by the parser +% +% :::{admonition} MyST colon fenced directive with arbitrary yaml metadata +% --- +% foo: +% bar: 1 +% --- +% +% Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +% incididunt ut labore et dolore magna aliqua. +% ::: + +. + +% Unknown colon-fenced directives are not yet implemented +% :::{exercise} +% This is an unknown admonition. +% ::: + +. + +::::{admonition} MyST colon fenced directive with two nested admonitions +:::{admonition} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +:::{admonition} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +:::{admonition} +truc +::: +:::: + +. + +::::{hint} A hint with alternating nested tips and texts +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. + +:::{tip} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. + +:::{tip} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +::: + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +:::: + +. + +- foo + :::{tip} A directive nested in bullet points + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. + :::