Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mdit_py_plugins/deflist/index.py: 97%
138 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:15 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:15 +0000
1"""Process definition lists."""
2from markdown_it import MarkdownIt
3from markdown_it.rules_block import StateBlock
5from mdit_py_plugins.utils import is_code_block
8def deflist_plugin(md: MarkdownIt) -> None:
9 """Plugin ported from
10 `markdown-it-deflist <https://github.com/markdown-it/markdown-it-deflist>`__.
12 The syntax is based on
13 `pandoc definition lists <http://johnmacfarlane.net/pandoc/README.html#definition-lists>`__:
15 .. code-block:: md
17 Term 1
18 : Definition 1 long form
20 second paragraph
22 Term 2 with *inline markup*
23 ~ Definition 2a compact style
24 ~ Definition 2b
26 """
28 def skipMarker(state: StateBlock, line: int) -> int:
29 """Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail."""
30 start = state.bMarks[line] + state.tShift[line]
31 maximum = state.eMarks[line]
33 if start >= maximum:
34 return -1
36 # Check bullet
37 marker = state.src[start]
38 start += 1
39 if marker != "~" and marker != ":":
40 return -1
42 pos = state.skipSpaces(start)
44 # require space after ":"
45 if start == pos:
46 return -1
48 # no empty definitions, e.g. " : "
49 if pos >= maximum:
50 return -1
52 return start
54 def markTightParagraphs(state: StateBlock, idx: int) -> None:
55 level = state.level + 2
57 i = idx + 2
58 l2 = len(state.tokens) - 2
59 while i < l2:
60 if (
61 state.tokens[i].level == level
62 and state.tokens[i].type == "paragraph_open"
63 ):
64 state.tokens[i + 2].hidden = True
65 state.tokens[i].hidden = True
66 i += 2
67 i += 1
69 def deflist(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
70 if is_code_block(state, startLine):
71 return False
73 if silent:
74 # quirk: validation mode validates a dd block only, not a whole deflist
75 if state.ddIndent < 0:
76 return False
77 return skipMarker(state, startLine) >= 0
79 nextLine = startLine + 1
80 if nextLine >= endLine:
81 return False
83 if state.isEmpty(nextLine):
84 nextLine += 1
85 if nextLine >= endLine:
86 return False
88 if state.sCount[nextLine] < state.blkIndent:
89 return False
90 contentStart = skipMarker(state, nextLine)
91 if contentStart < 0:
92 return False
94 # Start list
95 listTokIdx = len(state.tokens)
96 tight = True
98 token = state.push("dl_open", "dl", 1)
99 token.map = listLines = [startLine, 0]
101 # Iterate list items
102 dtLine = startLine
103 ddLine = nextLine
105 # One definition list can contain multiple DTs,
106 # and one DT can be followed by multiple DDs.
107 #
108 # Thus, there is two loops here, and label is
109 # needed to break out of the second one
110 #
111 break_outer = False
113 while True:
114 prevEmptyEnd = False
116 token = state.push("dt_open", "dt", 1)
117 token.map = [dtLine, dtLine]
119 token = state.push("inline", "", 0)
120 token.map = [dtLine, dtLine]
121 token.content = state.getLines(
122 dtLine, dtLine + 1, state.blkIndent, False
123 ).strip()
124 token.children = []
126 token = state.push("dt_close", "dt", -1)
128 while True:
129 token = state.push("dd_open", "dd", 1)
130 token.map = itemLines = [nextLine, 0]
132 pos = contentStart
133 maximum = state.eMarks[ddLine]
134 offset = (
135 state.sCount[ddLine]
136 + contentStart
137 - (state.bMarks[ddLine] + state.tShift[ddLine])
138 )
140 while pos < maximum:
141 if state.src[pos] == "\t":
142 offset += 4 - offset % 4
143 elif state.src[pos] == " ":
144 offset += 1
145 else:
146 break
148 pos += 1
150 contentStart = pos
152 oldTight = state.tight
153 oldDDIndent = state.ddIndent
154 oldIndent = state.blkIndent
155 oldTShift = state.tShift[ddLine]
156 oldSCount = state.sCount[ddLine]
157 oldParentType = state.parentType
158 state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2
159 state.tShift[ddLine] = contentStart - state.bMarks[ddLine]
160 state.sCount[ddLine] = offset
161 state.tight = True
162 state.parentType = "deflist"
164 state.md.block.tokenize(state, ddLine, endLine)
166 # If any of list item is tight, mark list as tight
167 if not state.tight or prevEmptyEnd:
168 tight = False
170 # Item become loose if finish with empty line,
171 # but we should filter last element, because it means list finish
172 prevEmptyEnd = (state.line - ddLine) > 1 and state.isEmpty(
173 state.line - 1
174 )
176 state.tShift[ddLine] = oldTShift
177 state.sCount[ddLine] = oldSCount
178 state.tight = oldTight
179 state.parentType = oldParentType
180 state.blkIndent = oldIndent
181 state.ddIndent = oldDDIndent
183 token = state.push("dd_close", "dd", -1)
185 itemLines[1] = nextLine = state.line
187 if nextLine >= endLine:
188 break_outer = True
189 break
191 if state.sCount[nextLine] < state.blkIndent:
192 break_outer = True
193 break
195 contentStart = skipMarker(state, nextLine)
196 if contentStart < 0:
197 break
199 ddLine = nextLine
201 # go to the next loop iteration:
202 # insert DD tag and repeat checking
204 if break_outer:
205 break_outer = False
206 break
208 if nextLine >= endLine:
209 break
210 dtLine = nextLine
212 if state.isEmpty(dtLine):
213 break
214 if state.sCount[dtLine] < state.blkIndent:
215 break
217 ddLine = dtLine + 1
218 if ddLine >= endLine:
219 break
220 if state.isEmpty(ddLine):
221 ddLine += 1
222 if ddLine >= endLine:
223 break
225 if state.sCount[ddLine] < state.blkIndent:
226 break
227 contentStart = skipMarker(state, ddLine)
228 if contentStart < 0:
229 break
231 # go to the next loop iteration:
232 # insert DT and DD tags and repeat checking
234 # Finalise list
235 token = state.push("dl_close", "dl", -1)
237 listLines[1] = nextLine
239 state.line = nextLine
241 # mark paragraphs tight if needed
242 if tight:
243 markTightParagraphs(state, listTokIdx)
245 return True
247 md.block.ruler.before(
248 "paragraph",
249 "deflist",
250 deflist,
251 {"alt": ["paragraph", "reference", "blockquote"]},
252 )