MyST-Parser
MyST-Parser copied to clipboard
"Empty" directives on 1 line?
Context
In some documents I have to write a lot of
```{exercise-end}
```
(This directive comes from sphinx-exercise.)
I don't understand why it cannot be written on 1 line, i.e. just
```{exercise-end}```
Proposal
A line with
```{exercise-end}```
could be parsed as an exercise-end
directive with no content.
Related question: What about ```{note} A very short note.```
on 1 line?
Tasks and updates
No response
Thanks for opening your first issue here! Engagement like this is essential for open source projects! :hugs:
If you haven't done so already, check out EBP's Code of Conduct. Also, please try to follow the issue template as it helps other community members to contribute more effectively.
If your issue is a feature request, others may react to it, to raise its prominence (see Feature Voting).
Welcome to the EBP community! :tada:
Heya thanks for the feedback.
The technical reason is that, in CommonMarkdown parsing (see https://markdown-it.github.io/)
```{exercise-end}
```
goes to
[
{
"type": "fence",
"tag": "code",
"attrs": null,
"map": [
0,
2
],
"nesting": 0,
"level": 0,
"children": null,
"content": "",
"markup": "```",
"info": "{exercise-end}",
"meta": null,
"block": true,
"hidden": false
}
]
whereas
```{exercise-end} text```
goes to:
[
{
"type": "paragraph_open",
"tag": "p",
"attrs": null,
"map": [
0,
1
],
"nesting": 1,
"level": 0,
"children": null,
"content": "",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false
},
{
"type": "inline",
"tag": "",
"attrs": null,
"map": [
0,
1
],
"nesting": 0,
"level": 1,
"children": [
{
"type": "code_inline",
"tag": "code",
"attrs": null,
"map": null,
"nesting": 0,
"level": 0,
"children": null,
"content": "{exercise-end} text",
"markup": "```",
"info": "",
"meta": null,
"block": false,
"hidden": false
}
],
"content": "```{exercise-end} text```",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false
},
{
"type": "paragraph_close",
"tag": "p",
"attrs": null,
"map": null,
"nesting": -1,
"level": 0,
"children": null,
"content": "",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false
}
]
so it is difficult to get these to act the same, and identify the one-line syntax.
This is not to say a one-line syntax would not be nice
Thanks for the answer! https://markdown-it.github.io/ is very convenient! And by the way, the executablebooks / MyST project is really great!
I wonder if there is a strong reason for this difference. I guess the definition in markdown of a code fence. I'm going to ask in markdown-it repo, since it seems to me that it would be the right place to change this behavior.
I got my answer, which is that indeed there is a strong reason for this difference for markdown-it. Any chance that myst-parser could support directive on 1 line with a simple Python filter? I mean something like this:
from pprint import pprint
import textwrap
from markdown_it import MarkdownIt
def filter_1line_fence(tokens):
result = []
t_first = None
t_second = None
t_third = None
for t in tokens:
t_first = t_second
t_second = t_third
t_third = t
if t_first is None:
continue
else:
if t_second is None:
result.append(t_first)
continue
if not (
t_first.type == "paragraph_open"
and t_second.type == "inline"
and t_third.type == "paragraph_close"
):
result.append(t_first)
continue
t_inline = t_second
if len(t_inline.children) > 1:
result.append(t_first)
continue
child = t_inline.children[0]
if child.type != "code_inline":
result.append(t_first)
continue
new_token = t_inline.__class__(
type="fence",
tag=child.tag,
nesting=0,
map=t_inline.map,
markup=child.markup,
info=child.content,
block=True,
)
result.append(new_token)
t_first = t_second = t_third = None
if t_second is not None:
result.append(t_second)
if t_third is not None:
result.append(t_third)
return result
def test_1line_fence():
md = MarkdownIt()
text = textwrap.dedent(
"""
start
```{toto} hello
```
stop
start
```{toto} hello```
stop
[link](target)
"""
)
tokens = md.parse(text)
result = filter_1line_fence(tokens)
assert len(result) == len(tokens) - 2
fences = [t for t in result if t.type == "fence"]
pprint(fences)
t_fence = fences[0]
t_fence_created = fences[1]
assert t_fence.map != t_fence_created.map
t_fence_created.map = t_fence.map
assert t_fence_created == t_fence
def test_no_1line_fence():
md = MarkdownIt()
text = textwrap.dedent(
"""
start
```{toto} hello
```
stop
start ```{toto} hello``` stop
A line with `name`.
[link](target)
"""
)
tokens = md.parse(text)
result = filter_1line_fence(tokens)
assert tokens == result