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