1from typing import TYPE_CHECKING, Match, Optional
2
3from ..helpers import PREVENT_BACKSLASH
4
5if TYPE_CHECKING:
6 from ..core import BaseRenderer, InlineState
7 from ..inline_parser import InlineParser
8 from ..markdown import Markdown
9
10__all__ = ["strikethrough", "mark", "insert", "superscript", "subscript"]
11
12SUPERSCRIPT_PATTERN = r"\^(?:" + PREVENT_BACKSLASH + r"\\\^|\S|\\ )+?\^"
13SUBSCRIPT_PATTERN = r"~(?:" + PREVENT_BACKSLASH + r"\\~|\S|\\ )+?~"
14
15
16def parse_strikethrough(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
17 return _parse_to_end(inline, m, state, "strikethrough", "~~")
18
19
20def render_strikethrough(renderer: "BaseRenderer", text: str) -> str:
21 return "<del>" + text + "</del>"
22
23
24def parse_mark(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
25 return _parse_to_end(inline, m, state, "mark", "==")
26
27
28def render_mark(renderer: "BaseRenderer", text: str) -> str:
29 return "<mark>" + text + "</mark>"
30
31
32def parse_insert(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
33 return _parse_to_end(inline, m, state, "insert", "^^")
34
35
36def render_insert(renderer: "BaseRenderer", text: str) -> str:
37 return "<ins>" + text + "</ins>"
38
39
40def parse_superscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
41 return _parse_script(inline, m, state, "superscript")
42
43
44def render_superscript(renderer: "BaseRenderer", text: str) -> str:
45 return "<sup>" + text + "</sup>"
46
47
48def parse_subscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
49 return _parse_script(inline, m, state, "subscript")
50
51
52def render_subscript(renderer: "BaseRenderer", text: str) -> str:
53 return "<sub>" + text + "</sub>"
54
55
56def _parse_to_end(
57 inline: "InlineParser",
58 m: Match[str],
59 state: "InlineState",
60 tok_type: str,
61 marker: str,
62) -> Optional[int]:
63 if state.depth >= inline.max_nested_level:
64 return None
65 pos = m.end()
66 close = state.delimiters.find(marker, pos, formatting=True)
67 if close is None:
68 return None
69 end_pos = close + len(marker)
70 text = state.src[pos:close]
71 new_state = state.copy()
72 new_state.src = text
73 children = inline.render(new_state)
74 state.append_token({"type": tok_type, "children": children})
75 return end_pos
76
77
78def _parse_script(inline: "InlineParser", m: Match[str], state: "InlineState", tok_type: str) -> Optional[int]:
79 if state.depth >= inline.max_nested_level:
80 return None
81 text = m.group(0)
82 new_state = state.copy()
83 new_state.src = text[1:-1].replace("\\ ", " ")
84 children = inline.render(new_state)
85 state.append_token({"type": tok_type, "children": children})
86 return m.end()
87
88
89def strikethrough(md: "Markdown") -> None:
90 """A mistune plugin to support strikethrough. Spec defined by
91 GitHub flavored Markdown and commonly used by many parsers:
92
93 .. code-block:: text
94
95 ~~This was mistaken text~~
96
97 It will be converted into HTML:
98
99 .. code-block:: html
100
101 <del>This was mistaken text</del>
102
103 :param md: Markdown instance
104 """
105 md.inline.register(
106 "strikethrough",
107 r"~~(?=[^\s~])",
108 parse_strikethrough,
109 before="link",
110 )
111 if md.renderer and md.renderer.NAME == "html":
112 md.renderer.register("strikethrough", render_strikethrough)
113
114
115def mark(md: "Markdown") -> None:
116 """A mistune plugin to add ``<mark>`` tag. Spec defined at
117 https://facelessuser.github.io/pymdown-extensions/extensions/mark/:
118
119 .. code-block:: text
120
121 ==mark me== ==mark \\=\\= equal==
122
123 :param md: Markdown instance
124 """
125 md.inline.register(
126 "mark",
127 r"==(?=[^\s=])",
128 parse_mark,
129 before="link",
130 )
131 if md.renderer and md.renderer.NAME == "html":
132 md.renderer.register("mark", render_mark)
133
134
135def insert(md: "Markdown") -> None:
136 """A mistune plugin to add ``<ins>`` tag. Spec defined at
137 https://facelessuser.github.io/pymdown-extensions/extensions/caret/#insert:
138
139 .. code-block:: text
140
141 ^^insert me^^
142
143 :param md: Markdown instance
144 """
145 md.inline.register(
146 "insert",
147 r"\^\^(?=[^\s\^])",
148 parse_insert,
149 before="link",
150 )
151 if md.renderer and md.renderer.NAME == "html":
152 md.renderer.register("insert", render_insert)
153
154
155def superscript(md: "Markdown") -> None:
156 """A mistune plugin to add ``<sup>`` tag. Spec defined at
157 https://pandoc.org/MANUAL.html#superscripts-and-subscripts:
158
159 .. code-block:: text
160
161 2^10^ is 1024.
162
163 :param md: Markdown instance
164 """
165 md.inline.register("superscript", SUPERSCRIPT_PATTERN, parse_superscript, before="linebreak")
166 if md.renderer and md.renderer.NAME == "html":
167 md.renderer.register("superscript", render_superscript)
168
169
170def subscript(md: "Markdown") -> None:
171 """A mistune plugin to add ``<sub>`` tag. Spec defined at
172 https://pandoc.org/MANUAL.html#superscripts-and-subscripts:
173
174 .. code-block:: text
175
176 H~2~O is a liquid.
177
178 :param md: Markdown instance
179 """
180 md.inline.register("subscript", SUBSCRIPT_PATTERN, parse_subscript, before="linebreak")
181 if md.renderer and md.renderer.NAME == "html":
182 md.renderer.register("subscript", render_subscript)