-
Notifications
You must be signed in to change notification settings - Fork 8
👌 IMPROVE: Support colon fenced directives #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a8b25c2
f7df460
98df6a4
3ddf25d
ac81145
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,11 +9,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: | ||
|
|
@@ -52,6 +111,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)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please run |
||
| # 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have something like this working locally: _YAML_METADATA_PATTERN = re.compile(r"^:\w[\w-]*:\s+.*$", re.MULTILINE)
def container_renderer(
node: RenderTreeNode, context: RenderContext, *args, **kwargs
) -> str:
children = node.children or []
parts = []
if children and children[0].type == "paragraph":
tokens = children[0].to_tokens()
yaml_lines = []
for token in tokens:
if token.type in {"paragraph_open", "paragraph_close"}:
continue
if token.content and _YAML_METADATA_PATTERN.match(token.content.strip()):
yaml_lines.append(token.content.strip())
elif yaml_lines:
break # Stop at first non-YAML line
if yaml_lines:
parts.append("\n".join(yaml_lines))
children = children[1:]
if children:
rendered_children = "\n\n".join(child.render(context) for child in children)
parts.append(rendered_children)
if parts:
content = "\n\n".join(parts)
return f"{node.markup}{node.info}\n\n{content}\n\n{node.markup}"
return f"{node.markup}{node.info}\n{node.markup}"
...
for directive in CONTAINER_DIRECTIVES: # Just minor nits
RENDERERS[f"container_{{{directive}}}"] = container_renderer |
||
|
|
||
|
|
||
| def _role_renderer(node: RenderTreeNode, context: RenderContext) -> str: | ||
| role_name = "{" + node.meta["name"] + "}" | ||
|
|
@@ -155,4 +244,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} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -504,3 +504,94 @@ MyST directive, no opts or content | |
| ```{some-directive} args | ||
| ``` | ||
| . | ||
|
|
||
| :::{admonition} MyST colon fenced directive with a title | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for adding tests! It looks like you may have some missing formatting, the repeating pattern is:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So something like: |
||
| 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. | ||
| ::: | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would want us to avoid the performance impact of creating each of these handlers
If we were to keep a hardcoded list, I would prefer to validate rather than search for specific matches (e.g. extract the directive name with regex like
[\w\-:]+, then if present in the set of allowed names, proceed) - or use regex conditional to search all variationsBut that's all blocked by the limitation of container_plugin being fixed strings only (no regex): https://github.com/executablebooks/mdit-py-plugins/blob/93fbf645108394bb10ecd392383e96c4e8902f30/mdit_py_plugins/container/index.py