Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/markdown_it/rules_inline/balance_pairs.py: 100%

61 statements  

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

1"""Balance paired characters (*, _, etc) in inline tokens.""" 

2from __future__ import annotations 

3 

4from .state_inline import Delimiter, StateInline 

5 

6 

7def processDelimiters(state: StateInline, delimiters: list[Delimiter]) -> None: 

8 """For each opening emphasis-like marker find a matching closing one.""" 

9 if not delimiters: 

10 return 

11 

12 openersBottom = {} 

13 maximum = len(delimiters) 

14 

15 # headerIdx is the first delimiter of the current (where closer is) delimiter run 

16 headerIdx = 0 

17 lastTokenIdx = -2 # needs any value lower than -1 

18 jumps: list[int] = [] 

19 closerIdx = 0 

20 while closerIdx < maximum: 

21 closer = delimiters[closerIdx] 

22 

23 jumps.append(0) 

24 

25 # markers belong to same delimiter run if: 

26 # - they have adjacent tokens 

27 # - AND markers are the same 

28 # 

29 if ( 

30 delimiters[headerIdx].marker != closer.marker 

31 or lastTokenIdx != closer.token - 1 

32 ): 

33 headerIdx = closerIdx 

34 lastTokenIdx = closer.token 

35 

36 # Length is only used for emphasis-specific "rule of 3", 

37 # if it's not defined (in strikethrough or 3rd party plugins), 

38 # we can default it to 0 to disable those checks. 

39 # 

40 closer.length = closer.length or 0 

41 

42 if not closer.close: 

43 closerIdx += 1 

44 continue 

45 

46 # Previously calculated lower bounds (previous fails) 

47 # for each marker, each delimiter length modulo 3, 

48 # and for whether this closer can be an opener; 

49 # https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460 

50 if closer.marker not in openersBottom: 

51 openersBottom[closer.marker] = [-1, -1, -1, -1, -1, -1] 

52 

53 minOpenerIdx = openersBottom[closer.marker][ 

54 (3 if closer.open else 0) + (closer.length % 3) 

55 ] 

56 

57 openerIdx = headerIdx - jumps[headerIdx] - 1 

58 

59 newMinOpenerIdx = openerIdx 

60 

61 while openerIdx > minOpenerIdx: 

62 opener = delimiters[openerIdx] 

63 

64 if opener.marker != closer.marker: 

65 openerIdx -= jumps[openerIdx] + 1 

66 continue 

67 

68 if opener.open and opener.end < 0: 

69 isOddMatch = False 

70 

71 # from spec: 

72 # 

73 # If one of the delimiters can both open and close emphasis, then the 

74 # sum of the lengths of the delimiter runs containing the opening and 

75 # closing delimiters must not be a multiple of 3 unless both lengths 

76 # are multiples of 3. 

77 # 

78 if ( 

79 (opener.close or closer.open) 

80 and ((opener.length + closer.length) % 3 == 0) 

81 and (opener.length % 3 != 0 or closer.length % 3 != 0) 

82 ): 

83 isOddMatch = True 

84 

85 if not isOddMatch: 

86 # If previous delimiter cannot be an opener, we can safely skip 

87 # the entire sequence in future checks. This is required to make 

88 # sure algorithm has linear complexity (see *_*_*_*_*_... case). 

89 # 

90 if openerIdx > 0 and not delimiters[openerIdx - 1].open: 

91 lastJump = jumps[openerIdx - 1] + 1 

92 else: 

93 lastJump = 0 

94 

95 jumps[closerIdx] = closerIdx - openerIdx + lastJump 

96 jumps[openerIdx] = lastJump 

97 

98 closer.open = False 

99 opener.end = closerIdx 

100 opener.close = False 

101 newMinOpenerIdx = -1 

102 

103 # treat next token as start of run, 

104 # it optimizes skips in **<...>**a**<...>** pathological case 

105 lastTokenIdx = -2 

106 

107 break 

108 

109 openerIdx -= jumps[openerIdx] + 1 

110 

111 if newMinOpenerIdx != -1: 

112 # If match for this delimiter run failed, we want to set lower bound for 

113 # future lookups. This is required to make sure algorithm has linear 

114 # complexity. 

115 # 

116 # See details here: 

117 # https:#github.com/commonmark/cmark/issues/178#issuecomment-270417442 

118 # 

119 openersBottom[closer.marker][ 

120 (3 if closer.open else 0) + ((closer.length or 0) % 3) 

121 ] = newMinOpenerIdx 

122 

123 closerIdx += 1 

124 

125 

126def link_pairs(state: StateInline) -> None: 

127 tokens_meta = state.tokens_meta 

128 maximum = len(state.tokens_meta) 

129 

130 processDelimiters(state, state.delimiters) 

131 

132 curr = 0 

133 while curr < maximum: 

134 curr_meta = tokens_meta[curr] 

135 if curr_meta and "delimiters" in curr_meta: 

136 processDelimiters(state, curr_meta["delimiters"]) 

137 curr += 1