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

139 statements  

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

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING 

4 

5from ..common.utils import isSpace 

6from ..ruler import StateBase 

7from ..token import Token 

8 

9if TYPE_CHECKING: 

10 from markdown_it.main import MarkdownIt 

11 

12 

13class StateBlock(StateBase): 

14 def __init__( 

15 self, 

16 src: str, 

17 md: MarkdownIt, 

18 env, 

19 tokens: list[Token], 

20 srcCharCode: tuple[int, ...] | None = None, 

21 ): 

22 if srcCharCode is not None: 

23 self._src = src 

24 self.srcCharCode = srcCharCode 

25 else: 

26 self.src = src 

27 

28 # link to parser instance 

29 self.md = md 

30 

31 self.env = env 

32 

33 # 

34 # Internal state variables 

35 # 

36 

37 self.tokens = tokens 

38 

39 self.bMarks = [] # line begin offsets for fast jumps 

40 self.eMarks = [] # line end offsets for fast jumps 

41 # offsets of the first non-space characters (tabs not expanded) 

42 self.tShift = [] 

43 self.sCount = [] # indents for each line (tabs expanded) 

44 

45 # An amount of virtual spaces (tabs expanded) between beginning 

46 # of each line (bMarks) and real beginning of that line. 

47 # 

48 # It exists only as a hack because blockquotes override bMarks 

49 # losing information in the process. 

50 # 

51 # It's used only when expanding tabs, you can think about it as 

52 # an initial tab length, e.g. bsCount=21 applied to string `\t123` 

53 # means first tab should be expanded to 4-21%4 === 3 spaces. 

54 # 

55 self.bsCount = [] 

56 

57 # block parser variables 

58 self.blkIndent = 0 # required block content indent (for example, if we are 

59 # inside a list, it would be positioned after list marker) 

60 self.line = 0 # line index in src 

61 self.lineMax = 0 # lines count 

62 self.tight = False # loose/tight mode for lists 

63 self.ddIndent = -1 # indent of the current dd block (-1 if there isn't any) 

64 self.listIndent = -1 # indent of the current list block (-1 if there isn't any) 

65 

66 # can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' 

67 # used in lists to determine if they interrupt a paragraph 

68 self.parentType = "root" 

69 

70 self.level = 0 

71 

72 # renderer 

73 self.result = "" 

74 

75 # Create caches 

76 # Generate markers. 

77 indent_found = False 

78 

79 start = pos = indent = offset = 0 

80 length = len(self.src) 

81 

82 for pos, character in enumerate(self.srcCharCode): 

83 if not indent_found: 

84 if isSpace(character): 

85 indent += 1 

86 

87 if character == 0x09: 

88 offset += 4 - offset % 4 

89 else: 

90 offset += 1 

91 continue 

92 else: 

93 indent_found = True 

94 

95 if character == 0x0A or pos == length - 1: 

96 if character != 0x0A: 

97 pos += 1 

98 self.bMarks.append(start) 

99 self.eMarks.append(pos) 

100 self.tShift.append(indent) 

101 self.sCount.append(offset) 

102 self.bsCount.append(0) 

103 

104 indent_found = False 

105 indent = 0 

106 offset = 0 

107 start = pos + 1 

108 

109 # Push fake entry to simplify cache bounds checks 

110 self.bMarks.append(length) 

111 self.eMarks.append(length) 

112 self.tShift.append(0) 

113 self.sCount.append(0) 

114 self.bsCount.append(0) 

115 

116 self.lineMax = len(self.bMarks) - 1 # don't count last fake line 

117 

118 def __repr__(self): 

119 return ( 

120 f"{self.__class__.__name__}" 

121 f"(line={self.line},level={self.level},tokens={len(self.tokens)})" 

122 ) 

123 

124 def push(self, ttype: str, tag: str, nesting: int) -> Token: 

125 """Push new token to "stream".""" 

126 token = Token(ttype, tag, nesting) 

127 token.block = True 

128 if nesting < 0: 

129 self.level -= 1 # closing tag 

130 token.level = self.level 

131 if nesting > 0: 

132 self.level += 1 # opening tag 

133 self.tokens.append(token) 

134 return token 

135 

136 def isEmpty(self, line: int) -> bool: 

137 """.""" 

138 return (self.bMarks[line] + self.tShift[line]) >= self.eMarks[line] 

139 

140 def skipEmptyLines(self, from_pos: int) -> int: 

141 """.""" 

142 while from_pos < self.lineMax: 

143 try: 

144 if (self.bMarks[from_pos] + self.tShift[from_pos]) < self.eMarks[ 

145 from_pos 

146 ]: 

147 break 

148 except IndexError: 

149 pass 

150 from_pos += 1 

151 return from_pos 

152 

153 def skipSpaces(self, pos: int) -> int: 

154 """Skip spaces from given position.""" 

155 while pos < len(self.src): 

156 if not isSpace(self.srcCharCode[pos]): 

157 break 

158 pos += 1 

159 return pos 

160 

161 def skipSpacesBack(self, pos: int, minimum: int) -> int: 

162 """Skip spaces from given position in reverse.""" 

163 if pos <= minimum: 

164 return pos 

165 while pos > minimum: 

166 pos -= 1 

167 if not isSpace(self.srcCharCode[pos]): 

168 return pos + 1 

169 return pos 

170 

171 def skipChars(self, pos: int, code: int) -> int: 

172 """Skip char codes from given position.""" 

173 while pos < len(self.src): 

174 if self.srcCharCode[pos] != code: 

175 break 

176 pos += 1 

177 return pos 

178 

179 def skipCharsBack(self, pos: int, code: int, minimum: int) -> int: 

180 """Skip char codes reverse from given position - 1.""" 

181 if pos <= minimum: 

182 return pos 

183 while pos > minimum: 

184 pos -= 1 

185 if code != self.srcCharCode[pos]: 

186 return pos + 1 

187 return pos 

188 

189 def getLines(self, begin: int, end: int, indent: int, keepLastLF: bool) -> str: 

190 """Cut lines range from source.""" 

191 line = begin 

192 if begin >= end: 

193 return "" 

194 

195 queue = [""] * (end - begin) 

196 

197 i = 1 

198 while line < end: 

199 lineIndent = 0 

200 lineStart = first = self.bMarks[line] 

201 if line + 1 < end or keepLastLF: 

202 last = self.eMarks[line] + 1 

203 else: 

204 last = self.eMarks[line] 

205 

206 while (first < last) and (lineIndent < indent): 

207 ch = self.srcCharCode[first] 

208 if isSpace(ch): 

209 if ch == 0x09: 

210 lineIndent += 4 - (lineIndent + self.bsCount[line]) % 4 

211 else: 

212 lineIndent += 1 

213 elif first - lineStart < self.tShift[line]: 

214 lineIndent += 1 

215 else: 

216 break 

217 first += 1 

218 

219 if lineIndent > indent: 

220 # partially expanding tabs in code blocks, e.g '\t\tfoobar' 

221 # with indent=2 becomes ' \tfoobar' 

222 queue[i - 1] = (" " * (lineIndent - indent)) + self.src[first:last] 

223 else: 

224 queue[i - 1] = self.src[first:last] 

225 

226 line += 1 

227 i += 1 

228 

229 return "".join(queue)