1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# Process admonitions and pass to cb.
import math
from typing import Callable, Optional, Tuple
from markdown_it import MarkdownIt
from markdown_it.rules_block import StateBlock
def get_tag(params: str) -> Tuple[str, str]:
if not params.strip():
return "", ""
tag, *_title = params.strip().split(" ")
joined = " ".join(_title)
title = ""
if not joined:
title = tag.title()
elif joined != '""':
title = joined
return tag.lower(), title
def validate(params: str) -> bool:
tag = params.strip().split(" ", 1)[-1] or ""
return bool(tag)
MIN_MARKERS = 3
MARKER_STR = "!"
MARKER_CHAR = ord(MARKER_STR)
MARKER_LEN = len(MARKER_STR)
def admonition(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
start = state.bMarks[startLine] + state.tShift[startLine]
maximum = state.eMarks[startLine]
# Check out the first character quickly, which should filter out most of non-containers
if MARKER_CHAR != ord(state.src[start]):
return False
# Check out the rest of the marker string
pos = start + 1
while pos <= maximum and MARKER_STR[(pos - start) % MARKER_LEN] == state.src[pos]:
pos += 1
marker_count = math.floor((pos - start) / MARKER_LEN)
if marker_count < MIN_MARKERS:
return False
marker_pos = pos - ((pos - start) % MARKER_LEN)
params = state.src[marker_pos:maximum]
markup = state.src[start:marker_pos]
if not validate(params):
return False
# Since start is found, we can report success here in validation mode
if silent:
return True
old_parent = state.parentType
old_line_max = state.lineMax
old_indent = state.blkIndent
blk_start = pos
while blk_start < maximum and state.src[blk_start] == " ":
blk_start += 1
state.parentType = "admonition"
state.blkIndent += blk_start - start
was_empty = False
# Search for the end of the block
next_line = startLine
while True:
next_line += 1
if next_line >= endLine:
# unclosed block should be autoclosed by end of document.
# also block seems to be autoclosed by end of parent
break
pos = state.bMarks[next_line] + state.tShift[next_line]
maximum = state.eMarks[next_line]
is_empty = state.sCount[next_line] < state.blkIndent
# two consecutive empty lines autoclose the block
if is_empty and was_empty:
break
was_empty = is_empty
if pos < maximum and state.sCount[next_line] < state.blkIndent:
# non-empty line with negative indent should stop the block:
# - !!!
# test
break
# this will prevent lazy continuations from ever going past our end marker
state.lineMax = next_line
tag, title = get_tag(params)
token = state.push("admonition_open", "div", 1)
token.markup = markup
token.block = True
token.attrs = {"class": f"admonition {tag}"}
token.meta = {"tag": tag}
token.content = title
token.info = params
token.map = [startLine, next_line]
if title:
title_markup = f"{markup} {tag}"
token = state.push("admonition_title_open", "p", 1)
token.markup = title_markup
token.attrs = {"class": "admonition-title"}
token.map = [startLine, startLine + 1]
token = state.push("inline", "", 0)
token.content = title
token.map = [startLine, startLine + 1]
token.children = []
token = state.push("admonition_title_close", "p", -1)
token.markup = title_markup
state.md.block.tokenize(state, startLine + 1, next_line)
token = state.push("admonition_close", "div", -1)
token.markup = state.src[start:pos]
token.block = True
state.parentType = old_parent
state.lineMax = old_line_max
state.blkIndent = old_indent
state.line = next_line
return True
def admon_plugin(md: MarkdownIt, render: Optional[Callable] = None) -> None:
"""Plugin to use
`python-markdown style admonitions
<https://python-markdown.github.io/extensions/admonition>`_.
.. code-block:: md
!!! note
*content*
Note, this is ported from
`markdown-it-admon
<https://github.com/commenthol/markdown-it-admon>`_.
"""
def renderDefault(self, tokens, idx, _options, env):
return self.renderToken(tokens, idx, _options, env)
render = render or renderDefault
md.add_render_rule("admonition_open", render)
md.add_render_rule("admonition_close", render)
md.add_render_rule("admonition_title_open", render)
md.add_render_rule("admonition_title_close", render)
md.block.ruler.before(
"fence",
"admonition",
admonition,
{"alt": ["paragraph", "reference", "blockquote", "list"]},
)
|