1from .state_inline import StateInline
2
3
4def fragments_join(state: StateInline) -> None:
5 """
6 Clean up tokens after emphasis and strikethrough postprocessing:
7 merge adjacent text nodes into one and re-calculate all token levels
8
9 This is necessary because initially emphasis delimiter markers (``*, _, ~``)
10 are treated as their own separate text tokens. Then emphasis rule either
11 leaves them as text (needed to merge with adjacent text) or turns them
12 into opening/closing tags (which messes up levels inside).
13 """
14 level = 0
15 maximum = len(state.tokens)
16
17 curr = last = 0
18 while curr < maximum:
19 # re-calculate levels after emphasis/strikethrough turns some text nodes
20 # into opening/closing tags
21 if state.tokens[curr].nesting < 0:
22 level -= 1 # closing tag
23 state.tokens[curr].level = level
24 if state.tokens[curr].nesting > 0:
25 level += 1 # opening tag
26
27 if (
28 state.tokens[curr].type == "text"
29 and curr + 1 < maximum
30 and state.tokens[curr + 1].type == "text"
31 ):
32 # Collapse a run of adjacent text nodes in a single join, instead
33 # of pairwise `a + b` concatenation. The pairwise form is O(L*k)
34 # in the size of the run because each step rebuilds the growing
35 # prefix; "".join is O(L).
36 parts = [state.tokens[curr].content]
37 curr += 1
38 while curr < maximum and state.tokens[curr].type == "text":
39 parts.append(state.tokens[curr].content)
40 curr += 1
41 merged = state.tokens[curr - 1]
42 merged.content = "".join(parts)
43 merged.level = level
44 state.tokens[last] = merged
45 last += 1
46 continue
47
48 if curr != last:
49 state.tokens[last] = state.tokens[curr]
50 last += 1
51 curr += 1
52
53 if curr != last:
54 del state.tokens[last:]