Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/markdown_it/rules_inline/balance_pairs.py: 98%
54 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:07 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:07 +0000
1# For each opening emphasis-like marker find a matching closing one
2#
3from .state_inline import StateInline
6def processDelimiters(state: StateInline, delimiters, *args):
7 openersBottom = {}
8 maximum = len(delimiters)
10 closerIdx = 0
11 while closerIdx < maximum:
12 closer = delimiters[closerIdx]
14 # Length is only used for emphasis-specific "rule of 3",
15 # if it's not defined (in strikethrough or 3rd party plugins),
16 # we can default it to 0 to disable those checks.
17 #
18 closer.length = closer.length or 0
20 if not closer.close:
21 closerIdx += 1
22 continue
24 # Previously calculated lower bounds (previous fails)
25 # for each marker, each delimiter length modulo 3,
26 # and for whether this closer can be an opener;
27 # https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460
28 if closer.marker not in openersBottom:
29 openersBottom[closer.marker] = [-1, -1, -1, -1, -1, -1]
31 minOpenerIdx = openersBottom[closer.marker][
32 (3 if closer.open else 0) + (closer.length % 3)
33 ]
35 openerIdx = closerIdx - closer.jump - 1
37 # avoid crash if `closer.jump` is pointing outside of the array,
38 # e.g. for strikethrough
39 if openerIdx < -1:
40 openerIdx = -1
42 newMinOpenerIdx = openerIdx
44 while openerIdx > minOpenerIdx:
45 opener = delimiters[openerIdx]
47 if opener.marker != closer.marker:
48 openerIdx -= opener.jump + 1
49 continue
51 if opener.open and opener.end < 0:
52 isOddMatch = False
54 # from spec:
55 #
56 # If one of the delimiters can both open and close emphasis, then the
57 # sum of the lengths of the delimiter runs containing the opening and
58 # closing delimiters must not be a multiple of 3 unless both lengths
59 # are multiples of 3.
60 #
61 if opener.close or closer.open:
62 if (opener.length + closer.length) % 3 == 0:
63 if opener.length % 3 != 0 or closer.length % 3 != 0:
64 isOddMatch = True
66 if not isOddMatch:
67 # If previous delimiter cannot be an opener, we can safely skip
68 # the entire sequence in future checks. This is required to make
69 # sure algorithm has linear complexity (see *_*_*_*_*_... case).
70 #
71 if openerIdx > 0 and not delimiters[openerIdx - 1].open:
72 lastJump = delimiters[openerIdx - 1].jump + 1
73 else:
74 lastJump = 0
76 closer.jump = closerIdx - openerIdx + lastJump
77 closer.open = False
78 opener.end = closerIdx
79 opener.jump = lastJump
80 opener.close = False
81 newMinOpenerIdx = -1
82 break
84 openerIdx -= opener.jump + 1
86 if newMinOpenerIdx != -1:
87 # If match for this delimiter run failed, we want to set lower bound for
88 # future lookups. This is required to make sure algorithm has linear
89 # complexity.
90 #
91 # See details here:
92 # https:#github.com/commonmark/cmark/issues/178#issuecomment-270417442
93 #
94 openersBottom[closer.marker][
95 (3 if closer.open else 0) + ((closer.length or 0) % 3)
96 ] = newMinOpenerIdx
98 closerIdx += 1
101def link_pairs(state: StateInline) -> None:
102 tokens_meta = state.tokens_meta
103 maximum = len(state.tokens_meta)
105 processDelimiters(state, state.delimiters)
107 curr = 0
108 while curr < maximum:
109 curr_meta = tokens_meta[curr]
110 if curr_meta and "delimiters" in curr_meta:
111 processDelimiters(state, curr_meta["delimiters"])
112 curr += 1