1# Process html tags
2from ..common.html_re import HTML_TAG_RE
3from ..common.utils import isLinkClose, isLinkOpen
4from .state_inline import StateInline
5
6
7def isLetter(ch: int) -> bool:
8 lc = ch | 0x20 # to lower case
9 # /* a */ and /* z */
10 return (lc >= 0x61) and (lc <= 0x7A)
11
12
13def html_inline(state: StateInline, silent: bool) -> bool:
14 pos = state.pos
15
16 if not state.md.options.get("html", None):
17 return False
18
19 # Check start
20 src = state.src
21 maximum = state.posMax
22 if src[pos] != "<" or pos + 2 >= maximum:
23 return False
24
25 # Quick fail on second char
26 ch = src[pos + 1]
27 if ch not in ("!", "?", "/") and not isLetter(ord(ch)): # /* / */
28 return False
29
30 # Every alternative of the tag regex ends with a specific terminator, at a
31 # known minimum offset from `pos`. If that terminator does not occur
32 # anywhere at or after this offset, no match is possible; bail out before
33 # running the regex, whose lazy sub-patterns would scan to the end of the
34 # input. Without this, a run of unterminated openers is quadratic.
35 if src.startswith("<!--", pos):
36 # `<!-->` / `<!--->`, else `<!--` ... `-->` (`<!---->` is the shortest)
37 if state.html_terminator_last(">") < pos + 4:
38 return False
39 if (
40 not src.startswith("<!-->", pos)
41 and not src.startswith("<!--->", pos)
42 and state.html_terminator_last("-->") < pos + 4
43 ):
44 return False
45 elif src.startswith("<![CDATA[", pos):
46 if state.html_terminator_last("]]>") < pos + 9: # `<![CDATA[]]>`
47 return False
48 elif ch == "?":
49 if state.html_terminator_last("?>") < pos + 2: # `<??>`
50 return False
51 elif ch == "!":
52 if state.html_terminator_last(">") < pos + 3: # `<!a>`
53 return False
54 elif state.html_terminator_last(">") < pos + 2: # `<a>`, `</a>`
55 return False
56
57 match = HTML_TAG_RE.match(src, pos)
58 if not match:
59 return False
60
61 if not silent:
62 token = state.push("html_inline", "", 0)
63 token.content = src[pos : pos + len(match.group(0))]
64
65 if isLinkOpen(token.content):
66 state.linkLevel += 1
67 if isLinkClose(token.content):
68 state.linkLevel -= 1
69
70 state.pos += len(match.group(0))
71 return True