Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/markdown_it/rules_block/reference.py: 94%

131 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:07 +0000

1import logging 

2 

3from ..common.utils import charCodeAt, isSpace, normalizeReference 

4from .state_block import StateBlock 

5 

6LOGGER = logging.getLogger(__name__) 

7 

8 

9def reference(state: StateBlock, startLine, _endLine, silent): 

10 LOGGER.debug( 

11 "entering reference: %s, %s, %s, %s", state, startLine, _endLine, silent 

12 ) 

13 

14 lines = 0 

15 pos = state.bMarks[startLine] + state.tShift[startLine] 

16 maximum = state.eMarks[startLine] 

17 nextLine = startLine + 1 

18 

19 # if it's indented more than 3 spaces, it should be a code block 

20 if state.sCount[startLine] - state.blkIndent >= 4: 

21 return False 

22 

23 if state.srcCharCode[pos] != 0x5B: # /* [ */ 

24 return False 

25 

26 # Simple check to quickly interrupt scan on [link](url) at the start of line. 

27 # Can be useful on practice: https:#github.com/markdown-it/markdown-it/issues/54 

28 while pos < maximum: 

29 # /* ] */ /* \ */ /* : */ 

30 if state.srcCharCode[pos] == 0x5D and state.srcCharCode[pos - 1] != 0x5C: 

31 if pos + 1 == maximum: 

32 return False 

33 if state.srcCharCode[pos + 1] != 0x3A: 

34 return False 

35 break 

36 pos += 1 

37 

38 endLine = state.lineMax 

39 

40 # jump line-by-line until empty one or EOF 

41 terminatorRules = state.md.block.ruler.getRules("reference") 

42 

43 oldParentType = state.parentType 

44 state.parentType = "reference" 

45 

46 while nextLine < endLine and not state.isEmpty(nextLine): 

47 # this would be a code block normally, but after paragraph 

48 # it's considered a lazy continuation regardless of what's there 

49 if state.sCount[nextLine] - state.blkIndent > 3: 

50 nextLine += 1 

51 continue 

52 

53 # quirk for blockquotes, this line should already be checked by that rule 

54 if state.sCount[nextLine] < 0: 

55 nextLine += 1 

56 continue 

57 

58 # Some tags can terminate paragraph without empty line. 

59 terminate = False 

60 for terminatorRule in terminatorRules: 

61 if terminatorRule(state, nextLine, endLine, True): 

62 terminate = True 

63 break 

64 

65 if terminate: 

66 break 

67 

68 nextLine += 1 

69 

70 string = state.getLines(startLine, nextLine, state.blkIndent, False).strip() 

71 maximum = len(string) 

72 

73 labelEnd = None 

74 pos = 1 

75 while pos < maximum: 

76 ch = charCodeAt(string, pos) 

77 if ch == 0x5B: # /* [ */ 

78 return False 

79 elif ch == 0x5D: # /* ] */ 

80 labelEnd = pos 

81 break 

82 elif ch == 0x0A: # /* \n */ 

83 lines += 1 

84 elif ch == 0x5C: # /* \ */ 

85 pos += 1 

86 if pos < maximum and charCodeAt(string, pos) == 0x0A: 

87 lines += 1 

88 pos += 1 

89 

90 if ( 

91 labelEnd is None or labelEnd < 0 or charCodeAt(string, labelEnd + 1) != 0x3A 

92 ): # /* : */ 

93 return False 

94 

95 # [label]: destination 'title' 

96 # ^^^ skip optional whitespace here 

97 pos = labelEnd + 2 

98 while pos < maximum: 

99 ch = charCodeAt(string, pos) 

100 if ch == 0x0A: 

101 lines += 1 

102 elif isSpace(ch): 

103 pass 

104 else: 

105 break 

106 pos += 1 

107 

108 # [label]: destination 'title' 

109 # ^^^^^^^^^^^ parse this 

110 res = state.md.helpers.parseLinkDestination(string, pos, maximum) 

111 if not res.ok: 

112 return False 

113 

114 href = state.md.normalizeLink(res.str) 

115 if not state.md.validateLink(href): 

116 return False 

117 

118 pos = res.pos 

119 lines += res.lines 

120 

121 # save cursor state, we could require to rollback later 

122 destEndPos = pos 

123 destEndLineNo = lines 

124 

125 # [label]: destination 'title' 

126 # ^^^ skipping those spaces 

127 start = pos 

128 while pos < maximum: 

129 ch = charCodeAt(string, pos) 

130 if ch == 0x0A: 

131 lines += 1 

132 elif isSpace(ch): 

133 pass 

134 else: 

135 break 

136 pos += 1 

137 

138 # [label]: destination 'title' 

139 # ^^^^^^^ parse this 

140 res = state.md.helpers.parseLinkTitle(string, pos, maximum) 

141 if pos < maximum and start != pos and res.ok: 

142 title = res.str 

143 pos = res.pos 

144 lines += res.lines 

145 else: 

146 title = "" 

147 pos = destEndPos 

148 lines = destEndLineNo 

149 

150 # skip trailing spaces until the rest of the line 

151 while pos < maximum: 

152 ch = charCodeAt(string, pos) 

153 if not isSpace(ch): 

154 break 

155 pos += 1 

156 

157 if pos < maximum and charCodeAt(string, pos) != 0x0A: 

158 if title: 

159 # garbage at the end of the line after title, 

160 # but it could still be a valid reference if we roll back 

161 title = "" 

162 pos = destEndPos 

163 lines = destEndLineNo 

164 while pos < maximum: 

165 ch = charCodeAt(string, pos) 

166 if not isSpace(ch): 

167 break 

168 pos += 1 

169 

170 if pos < maximum and charCodeAt(string, pos) != 0x0A: 

171 # garbage at the end of the line 

172 return False 

173 

174 label = normalizeReference(string[1:labelEnd]) 

175 if not label: 

176 # CommonMark 0.20 disallows empty labels 

177 return False 

178 

179 # Reference can not terminate anything. This check is for safety only. 

180 if silent: 

181 return True 

182 

183 if "references" not in state.env: 

184 state.env["references"] = {} 

185 

186 state.line = startLine + lines + 1 

187 

188 # note, this is not part of markdown-it JS, but is useful for renderers 

189 if state.md.options.get("inline_definitions", False): 

190 token = state.push("definition", "", 0) 

191 token.meta = { 

192 "id": label, 

193 "title": title, 

194 "url": href, 

195 "label": string[1:labelEnd], 

196 } 

197 token.map = [startLine, state.line] 

198 

199 if label not in state.env["references"]: 

200 state.env["references"][label] = { 

201 "title": title, 

202 "href": href, 

203 "map": [startLine, state.line], 

204 } 

205 else: 

206 state.env.setdefault("duplicate_refs", []).append( 

207 { 

208 "title": title, 

209 "href": href, 

210 "label": label, 

211 "map": [startLine, state.line], 

212 } 

213 ) 

214 

215 state.parentType = oldParentType 

216 

217 return True