Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mdit_py_plugins/deflist/index.py: 97%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

138 statements  

1"""Process definition lists.""" 

2 

3from markdown_it import MarkdownIt 

4from markdown_it.rules_block import StateBlock 

5 

6from mdit_py_plugins.utils import is_code_block 

7 

8 

9def deflist_plugin(md: MarkdownIt) -> None: 

10 """Plugin ported from 

11 `markdown-it-deflist <https://github.com/markdown-it/markdown-it-deflist>`__. 

12 

13 The syntax is based on 

14 `pandoc definition lists <http://johnmacfarlane.net/pandoc/README.html#definition-lists>`__: 

15 

16 .. code-block:: md 

17 

18 Term 1 

19 : Definition 1 long form 

20 

21 second paragraph 

22 

23 Term 2 with *inline markup* 

24 ~ Definition 2a compact style 

25 ~ Definition 2b 

26 

27 """ 

28 

29 def skipMarker(state: StateBlock, line: int) -> int: 

30 """Search `[:~][\n ]`, returns next pos after marker on success or -1 on fail.""" 

31 start = state.bMarks[line] + state.tShift[line] 

32 maximum = state.eMarks[line] 

33 

34 if start >= maximum: 

35 return -1 

36 

37 # Check bullet 

38 marker = state.src[start] 

39 start += 1 

40 if marker != "~" and marker != ":": 

41 return -1 

42 

43 pos = state.skipSpaces(start) 

44 

45 # require space after ":" 

46 if start == pos: 

47 return -1 

48 

49 # no empty definitions, e.g. " : " 

50 if pos >= maximum: 

51 return -1 

52 

53 return start 

54 

55 def markTightParagraphs(state: StateBlock, idx: int) -> None: 

56 level = state.level + 2 

57 

58 i = idx + 2 

59 l2 = len(state.tokens) - 2 

60 while i < l2: 

61 if ( 

62 state.tokens[i].level == level 

63 and state.tokens[i].type == "paragraph_open" 

64 ): 

65 state.tokens[i + 2].hidden = True 

66 state.tokens[i].hidden = True 

67 i += 2 

68 i += 1 

69 

70 def deflist(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool: 

71 if is_code_block(state, startLine): 

72 return False 

73 

74 if silent: 

75 # quirk: validation mode validates a dd block only, not a whole deflist 

76 if state.ddIndent < 0: 

77 return False 

78 return skipMarker(state, startLine) >= 0 

79 

80 nextLine = startLine + 1 

81 if nextLine >= endLine: 

82 return False 

83 

84 if state.isEmpty(nextLine): 

85 nextLine += 1 

86 if nextLine >= endLine: 

87 return False 

88 

89 if state.sCount[nextLine] < state.blkIndent: 

90 return False 

91 contentStart = skipMarker(state, nextLine) 

92 if contentStart < 0: 

93 return False 

94 

95 # Start list 

96 listTokIdx = len(state.tokens) 

97 tight = True 

98 

99 token = state.push("dl_open", "dl", 1) 

100 token.map = listLines = [startLine, 0] 

101 

102 # Iterate list items 

103 dtLine = startLine 

104 ddLine = nextLine 

105 

106 # One definition list can contain multiple DTs, 

107 # and one DT can be followed by multiple DDs. 

108 # 

109 # Thus, there is two loops here, and label is 

110 # needed to break out of the second one 

111 # 

112 break_outer = False 

113 

114 while True: 

115 prevEmptyEnd = False 

116 

117 token = state.push("dt_open", "dt", 1) 

118 token.map = [dtLine, dtLine] 

119 

120 token = state.push("inline", "", 0) 

121 token.map = [dtLine, dtLine] 

122 token.content = state.getLines( 

123 dtLine, dtLine + 1, state.blkIndent, False 

124 ).strip() 

125 token.children = [] 

126 

127 token = state.push("dt_close", "dt", -1) 

128 

129 while True: 

130 token = state.push("dd_open", "dd", 1) 

131 token.map = itemLines = [nextLine, 0] 

132 

133 pos = contentStart 

134 maximum = state.eMarks[ddLine] 

135 offset = ( 

136 state.sCount[ddLine] 

137 + contentStart 

138 - (state.bMarks[ddLine] + state.tShift[ddLine]) 

139 ) 

140 

141 while pos < maximum: 

142 if state.src[pos] == "\t": 

143 offset += 4 - offset % 4 

144 elif state.src[pos] == " ": 

145 offset += 1 

146 else: 

147 break 

148 

149 pos += 1 

150 

151 contentStart = pos 

152 

153 oldTight = state.tight 

154 oldDDIndent = state.ddIndent 

155 oldIndent = state.blkIndent 

156 oldTShift = state.tShift[ddLine] 

157 oldSCount = state.sCount[ddLine] 

158 oldParentType = state.parentType 

159 state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2 

160 state.tShift[ddLine] = contentStart - state.bMarks[ddLine] 

161 state.sCount[ddLine] = offset 

162 state.tight = True 

163 state.parentType = "deflist" 

164 

165 state.md.block.tokenize(state, ddLine, endLine) 

166 

167 # If any of list item is tight, mark list as tight 

168 if not state.tight or prevEmptyEnd: 

169 tight = False 

170 

171 # Item become loose if finish with empty line, 

172 # but we should filter last element, because it means list finish 

173 prevEmptyEnd = (state.line - ddLine) > 1 and state.isEmpty( 

174 state.line - 1 

175 ) 

176 

177 state.tShift[ddLine] = oldTShift 

178 state.sCount[ddLine] = oldSCount 

179 state.tight = oldTight 

180 state.parentType = oldParentType 

181 state.blkIndent = oldIndent 

182 state.ddIndent = oldDDIndent 

183 

184 token = state.push("dd_close", "dd", -1) 

185 

186 itemLines[1] = nextLine = state.line 

187 

188 if nextLine >= endLine: 

189 break_outer = True 

190 break 

191 

192 if state.sCount[nextLine] < state.blkIndent: 

193 break_outer = True 

194 break 

195 

196 contentStart = skipMarker(state, nextLine) 

197 if contentStart < 0: 

198 break 

199 

200 ddLine = nextLine 

201 

202 # go to the next loop iteration: 

203 # insert DD tag and repeat checking 

204 

205 if break_outer: 

206 break_outer = False 

207 break 

208 

209 if nextLine >= endLine: 

210 break 

211 dtLine = nextLine 

212 

213 if state.isEmpty(dtLine): 

214 break 

215 if state.sCount[dtLine] < state.blkIndent: 

216 break 

217 

218 ddLine = dtLine + 1 

219 if ddLine >= endLine: 

220 break 

221 if state.isEmpty(ddLine): 

222 ddLine += 1 

223 if ddLine >= endLine: 

224 break 

225 

226 if state.sCount[ddLine] < state.blkIndent: 

227 break 

228 contentStart = skipMarker(state, ddLine) 

229 if contentStart < 0: 

230 break 

231 

232 # go to the next loop iteration: 

233 # insert DT and DD tags and repeat checking 

234 

235 # Finalise list 

236 token = state.push("dl_close", "dl", -1) 

237 

238 listLines[1] = nextLine 

239 

240 state.line = nextLine 

241 

242 # mark paragraphs tight if needed 

243 if tight: 

244 markTightParagraphs(state, listTokIdx) 

245 

246 return True 

247 

248 md.block.ruler.before( 

249 "paragraph", 

250 "deflist", 

251 deflist, 

252 {"alt": ["paragraph", "reference", "blockquote"]}, 

253 )