Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mistune/plugins/formatting.py: 63%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

95 statements  

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") -> 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") -> 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 pos = m.end() 

64 cache_key = (id(state.src), marker) 

65 cache = state.formatting_no_end.get(cache_key) 

66 if cache is not None and cache[0] is state.src and pos <= cache[1]: 

67 return None 

68 

69 end_pos = _find_end_marker(state.src, pos, marker) 

70 if end_pos is None: 

71 state.formatting_no_end[cache_key] = (state.src, len(state.src)) 

72 return None 

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 _find_end_marker(src: str, pos: int, marker: str) -> Optional[int]: 

82 c = marker[0] 

83 marker_len = len(marker) 

84 end = src.find(marker, pos) 

85 while end != -1: 

86 marker_end = end + marker_len 

87 if marker_end < len(src) and src[marker_end] == c and not src.startswith(marker, marker_end): 

88 end = src.find(marker, end + 1) 

89 continue 

90 

91 if end > pos: 

92 prev = src[end - 1] 

93 escaped_marker_before = ( 

94 prev == c and end >= pos + 2 and src[end - 2] == "\\" and not _has_odd_backslashes(src, end - 2) 

95 ) 

96 if (not prev.isspace() and prev != c) or escaped_marker_before: 

97 return marker_end 

98 

99 end = src.find(marker, end + 1) 

100 return None 

101 

102 

103def _has_odd_backslashes(src: str, pos: int) -> bool: 

104 count = 0 

105 pos -= 1 

106 while pos >= 0 and src[pos] == "\\": 

107 count += 1 

108 pos -= 1 

109 return count % 2 == 1 

110 

111 

112def _parse_script(inline: "InlineParser", m: Match[str], state: "InlineState", tok_type: str) -> int: 

113 text = m.group(0) 

114 new_state = state.copy() 

115 new_state.src = text[1:-1].replace("\\ ", " ") 

116 children = inline.render(new_state) 

117 state.append_token({"type": tok_type, "children": children}) 

118 return m.end() 

119 

120 

121def strikethrough(md: "Markdown") -> None: 

122 """A mistune plugin to support strikethrough. Spec defined by 

123 GitHub flavored Markdown and commonly used by many parsers: 

124 

125 .. code-block:: text 

126 

127 ~~This was mistaken text~~ 

128 

129 It will be converted into HTML: 

130 

131 .. code-block:: html 

132 

133 <del>This was mistaken text</del> 

134 

135 :param md: Markdown instance 

136 """ 

137 md.inline.register( 

138 "strikethrough", 

139 r"~~(?=[^\s~])", 

140 parse_strikethrough, 

141 before="link", 

142 ) 

143 if md.renderer and md.renderer.NAME == "html": 

144 md.renderer.register("strikethrough", render_strikethrough) 

145 

146 

147def mark(md: "Markdown") -> None: 

148 """A mistune plugin to add ``<mark>`` tag. Spec defined at 

149 https://facelessuser.github.io/pymdown-extensions/extensions/mark/: 

150 

151 .. code-block:: text 

152 

153 ==mark me== ==mark \\=\\= equal== 

154 

155 :param md: Markdown instance 

156 """ 

157 md.inline.register( 

158 "mark", 

159 r"==(?=[^\s=])", 

160 parse_mark, 

161 before="link", 

162 ) 

163 if md.renderer and md.renderer.NAME == "html": 

164 md.renderer.register("mark", render_mark) 

165 

166 

167def insert(md: "Markdown") -> None: 

168 """A mistune plugin to add ``<ins>`` tag. Spec defined at 

169 https://facelessuser.github.io/pymdown-extensions/extensions/caret/#insert: 

170 

171 .. code-block:: text 

172 

173 ^^insert me^^ 

174 

175 :param md: Markdown instance 

176 """ 

177 md.inline.register( 

178 "insert", 

179 r"\^\^(?=[^\s\^])", 

180 parse_insert, 

181 before="link", 

182 ) 

183 if md.renderer and md.renderer.NAME == "html": 

184 md.renderer.register("insert", render_insert) 

185 

186 

187def superscript(md: "Markdown") -> None: 

188 """A mistune plugin to add ``<sup>`` tag. Spec defined at 

189 https://pandoc.org/MANUAL.html#superscripts-and-subscripts: 

190 

191 .. code-block:: text 

192 

193 2^10^ is 1024. 

194 

195 :param md: Markdown instance 

196 """ 

197 md.inline.register("superscript", SUPERSCRIPT_PATTERN, parse_superscript, before="linebreak") 

198 if md.renderer and md.renderer.NAME == "html": 

199 md.renderer.register("superscript", render_superscript) 

200 

201 

202def subscript(md: "Markdown") -> None: 

203 """A mistune plugin to add ``<sub>`` tag. Spec defined at 

204 https://pandoc.org/MANUAL.html#superscripts-and-subscripts: 

205 

206 .. code-block:: text 

207 

208 H~2~O is a liquid. 

209 

210 :param md: Markdown instance 

211 """ 

212 md.inline.register("subscript", SUBSCRIPT_PATTERN, parse_subscript, before="linebreak") 

213 if md.renderer and md.renderer.NAME == "html": 

214 md.renderer.register("subscript", render_subscript)