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

1"""Process definition lists.""" 

2from markdown_it import MarkdownIt 

3from markdown_it.rules_block import StateBlock 

4 

5from mdit_py_plugins.utils import is_code_block 

6 

7 

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

9 """Plugin ported from 

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

11 

12 The syntax is based on 

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

14 

15 .. code-block:: md 

16 

17 Term 1 

18 : Definition 1 long form 

19 

20 second paragraph 

21 

22 Term 2 with *inline markup* 

23 ~ Definition 2a compact style 

24 ~ Definition 2b 

25 

26 """ 

27 

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] 

32 

33 if start >= maximum: 

34 return -1 

35 

36 # Check bullet 

37 marker = state.src[start] 

38 start += 1 

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

40 return -1 

41 

42 pos = state.skipSpaces(start) 

43 

44 # require space after ":" 

45 if start == pos: 

46 return -1 

47 

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

49 if pos >= maximum: 

50 return -1 

51 

52 return start 

53 

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

55 level = state.level + 2 

56 

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 

68 

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

70 if is_code_block(state, startLine): 

71 return False 

72 

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 

78 

79 nextLine = startLine + 1 

80 if nextLine >= endLine: 

81 return False 

82 

83 if state.isEmpty(nextLine): 

84 nextLine += 1 

85 if nextLine >= endLine: 

86 return False 

87 

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

89 return False 

90 contentStart = skipMarker(state, nextLine) 

91 if contentStart < 0: 

92 return False 

93 

94 # Start list 

95 listTokIdx = len(state.tokens) 

96 tight = True 

97 

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

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

100 

101 # Iterate list items 

102 dtLine = startLine 

103 ddLine = nextLine 

104 

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 

112 

113 while True: 

114 prevEmptyEnd = False 

115 

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

117 token.map = [dtLine, dtLine] 

118 

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 = [] 

125 

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

127 

128 while True: 

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

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

131 

132 pos = contentStart 

133 maximum = state.eMarks[ddLine] 

134 offset = ( 

135 state.sCount[ddLine] 

136 + contentStart 

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

138 ) 

139 

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 

147 

148 pos += 1 

149 

150 contentStart = pos 

151 

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" 

163 

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

165 

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

167 if not state.tight or prevEmptyEnd: 

168 tight = False 

169 

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 ) 

175 

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 

182 

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

184 

185 itemLines[1] = nextLine = state.line 

186 

187 if nextLine >= endLine: 

188 break_outer = True 

189 break 

190 

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

192 break_outer = True 

193 break 

194 

195 contentStart = skipMarker(state, nextLine) 

196 if contentStart < 0: 

197 break 

198 

199 ddLine = nextLine 

200 

201 # go to the next loop iteration: 

202 # insert DD tag and repeat checking 

203 

204 if break_outer: 

205 break_outer = False 

206 break 

207 

208 if nextLine >= endLine: 

209 break 

210 dtLine = nextLine 

211 

212 if state.isEmpty(dtLine): 

213 break 

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

215 break 

216 

217 ddLine = dtLine + 1 

218 if ddLine >= endLine: 

219 break 

220 if state.isEmpty(ddLine): 

221 ddLine += 1 

222 if ddLine >= endLine: 

223 break 

224 

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

226 break 

227 contentStart = skipMarker(state, ddLine) 

228 if contentStart < 0: 

229 break 

230 

231 # go to the next loop iteration: 

232 # insert DT and DD tags and repeat checking 

233 

234 # Finalise list 

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

236 

237 listLines[1] = nextLine 

238 

239 state.line = nextLine 

240 

241 # mark paragraphs tight if needed 

242 if tight: 

243 markTightParagraphs(state, listTokIdx) 

244 

245 return True 

246 

247 md.block.ruler.before( 

248 "paragraph", 

249 "deflist", 

250 deflist, 

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

252 )