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
« 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
4from .state_inline import Delimiter, StateInline
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
12 openersBottom = {}
13 maximum = len(delimiters)
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]
23 jumps.append(0)
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
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
42 if not closer.close:
43 closerIdx += 1
44 continue
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]
53 minOpenerIdx = openersBottom[closer.marker][
54 (3 if closer.open else 0) + (closer.length % 3)
55 ]
57 openerIdx = headerIdx - jumps[headerIdx] - 1
59 newMinOpenerIdx = openerIdx
61 while openerIdx > minOpenerIdx:
62 opener = delimiters[openerIdx]
64 if opener.marker != closer.marker:
65 openerIdx -= jumps[openerIdx] + 1
66 continue
68 if opener.open and opener.end < 0:
69 isOddMatch = False
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
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
95 jumps[closerIdx] = closerIdx - openerIdx + lastJump
96 jumps[openerIdx] = lastJump
98 closer.open = False
99 opener.end = closerIdx
100 opener.close = False
101 newMinOpenerIdx = -1
103 # treat next token as start of run,
104 # it optimizes skips in **<...>**a**<...>** pathological case
105 lastTokenIdx = -2
107 break
109 openerIdx -= jumps[openerIdx] + 1
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
123 closerIdx += 1
126def link_pairs(state: StateInline) -> None:
127 tokens_meta = state.tokens_meta
128 maximum = len(state.tokens_meta)
130 processDelimiters(state, state.delimiters)
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