Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/markdown_it/parser_inline.py: 98%
61 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"""Tokenizes paragraph content.
2"""
3from __future__ import annotations
5from . import rules_inline
6from .ruler import RuleFunc, Ruler
7from .rules_inline.state_inline import StateInline
8from .token import Token
10# Parser rules
11_rules: list[tuple[str, RuleFunc]] = [
12 ("text", rules_inline.text),
13 ("newline", rules_inline.newline),
14 ("escape", rules_inline.escape),
15 ("backticks", rules_inline.backtick),
16 ("strikethrough", rules_inline.strikethrough.tokenize),
17 ("emphasis", rules_inline.emphasis.tokenize),
18 ("link", rules_inline.link),
19 ("image", rules_inline.image),
20 ("autolink", rules_inline.autolink),
21 ("html_inline", rules_inline.html_inline),
22 ("entity", rules_inline.entity),
23]
25_rules2: list[tuple[str, RuleFunc]] = [
26 ("balance_pairs", rules_inline.link_pairs),
27 ("strikethrough", rules_inline.strikethrough.postProcess),
28 ("emphasis", rules_inline.emphasis.postProcess),
29 ("text_collapse", rules_inline.text_collapse),
30]
33class ParserInline:
34 def __init__(self):
35 self.ruler = Ruler()
36 for name, rule in _rules:
37 self.ruler.push(name, rule)
38 # Second ruler used for post-processing (e.g. in emphasis-like rules)
39 self.ruler2 = Ruler()
40 for name, rule2 in _rules2:
41 self.ruler2.push(name, rule2)
43 def skipToken(self, state: StateInline) -> None:
44 """Skip single token by running all rules in validation mode;
45 returns `True` if any rule reported success
46 """
47 ok = False
48 pos = state.pos
49 rules = self.ruler.getRules("")
50 maxNesting = state.md.options["maxNesting"]
51 cache = state.cache
53 if pos in cache:
54 state.pos = cache[pos]
55 return
57 if state.level < maxNesting:
58 for rule in rules:
59 # Increment state.level and decrement it later to limit recursion.
60 # It's harmless to do here, because no tokens are created.
61 # But ideally, we'd need a separate private state variable for this purpose.
62 state.level += 1
63 ok = rule(state, True)
64 state.level -= 1
65 if ok:
66 break
67 else:
68 # Too much nesting, just skip until the end of the paragraph.
69 #
70 # NOTE: this will cause links to behave incorrectly in the following case,
71 # when an amount of `[` is exactly equal to `maxNesting + 1`:
72 #
73 # [[[[[[[[[[[[[[[[[[[[[foo]()
74 #
75 # TODO: remove this workaround when CM standard will allow nested links
76 # (we can replace it by preventing links from being parsed in
77 # validation mode)
78 #
79 state.pos = state.posMax
81 if not ok:
82 state.pos += 1
83 cache[pos] = state.pos
85 def tokenize(self, state: StateInline) -> None:
86 """Generate tokens for input range."""
87 ok = False
88 rules = self.ruler.getRules("")
89 end = state.posMax
90 maxNesting = state.md.options["maxNesting"]
92 while state.pos < end:
93 # Try all possible rules.
94 # On success, rule should:
95 #
96 # - update `state.pos`
97 # - update `state.tokens`
98 # - return true
100 if state.level < maxNesting:
101 for rule in rules:
102 ok = rule(state, False)
103 if ok:
104 break
106 if ok:
107 if state.pos >= end:
108 break
109 continue
111 state.pending += state.src[state.pos]
112 state.pos += 1
114 if state.pending:
115 state.pushPending()
117 def parse(self, src: str, md, env, tokens: list[Token]) -> list[Token]:
118 """Process input string and push inline tokens into `tokens`"""
119 state = StateInline(src, md, env, tokens)
120 self.tokenize(state)
121 rules2 = self.ruler2.getRules("")
122 for rule in rules2:
123 rule(state)
124 return state.tokens