Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mdit_py_plugins/amsmath/__init__.py: 50%

52 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:15 +0000

1"""An extension to capture amsmath latex environments.""" 

2from __future__ import annotations 

3 

4import re 

5from typing import TYPE_CHECKING, Callable, Optional, Sequence 

6 

7from markdown_it import MarkdownIt 

8from markdown_it.common.utils import escapeHtml 

9from markdown_it.rules_block import StateBlock 

10 

11from mdit_py_plugins.utils import is_code_block 

12 

13if TYPE_CHECKING: 

14 from markdown_it.renderer import RendererProtocol 

15 from markdown_it.token import Token 

16 from markdown_it.utils import EnvType, OptionsDict 

17 

18# Taken from amsmath version 2.1 

19# http://anorien.csc.warwick.ac.uk/mirrors/CTAN/macros/latex/required/amsmath/amsldoc.pdf 

20ENVIRONMENTS = [ 

21 # 3.2 single equation with an automatically gen-erated number 

22 "equation", 

23 # 3.3 variation equation, used for equations that dont fit on a single line 

24 "multline", 

25 # 3.5 a group of consecutive equations when there is no alignment desired among them 

26 "gather", 

27 # 3.6 Used for two or more equations when vertical alignment is desired 

28 "align", 

29 # allows the horizontal space between equationsto be explicitly specified. 

30 "alignat", 

31 # stretches the space betweenthe equation columns to the maximum possible width 

32 "flalign", 

33 # 4.1 The pmatrix, bmatrix, Bmatrix, vmatrix and Vmatrix have (respectively) 

34 # (),[],{},||,and ‖‖ delimiters built in. 

35 "matrix", 

36 "pmatrix", 

37 "bmatrix", 

38 "Bmatrix", 

39 "vmatrix", 

40 "Vmatrix", 

41 # eqnarray is another math environment, it is not part of amsmath, 

42 # and note that it is better to use align or equation+split instead 

43 "eqnarray", 

44] 

45# other "non-top-level" environments: 

46 

47# 3.4 the split environment is for single equations that are too long to fit on one line 

48# and hence must be split into multiple lines, 

49# it is intended for use only inside some other displayed equation structure, 

50# usually an equation, align, or gather environment 

51 

52# 3.7 variants gathered, aligned,and alignedat are provided 

53# whose total width is the actual width of the contents; 

54# thus they can be used as a component in a containing expression 

55 

56RE_OPEN = re.compile(r"\\begin\{(" + "|".join(ENVIRONMENTS) + r")([\*]?)\}") 

57 

58 

59def amsmath_plugin( 

60 md: MarkdownIt, *, renderer: Optional[Callable[[str], str]] = None 

61) -> None: 

62 """Parses TeX math equations, without any surrounding delimiters, 

63 only for top-level `amsmath <https://ctan.org/pkg/amsmath>`__ environments: 

64 

65 .. code-block:: latex 

66 

67 \\begin{gather*} 

68 a_1=b_1+c_1\\\\ 

69 a_2=b_2+c_2-d_2+e_2 

70 \\end{gather*} 

71 

72 :param renderer: Function to render content, by default escapes HTML 

73 

74 """ 

75 md.block.ruler.before( 

76 "blockquote", 

77 "amsmath", 

78 amsmath_block, 

79 {"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]}, 

80 ) 

81 

82 _renderer = (lambda content: escapeHtml(content)) if renderer is None else renderer 

83 

84 def render_amsmath_block( 

85 self: RendererProtocol, 

86 tokens: Sequence[Token], 

87 idx: int, 

88 options: OptionsDict, 

89 env: EnvType, 

90 ) -> str: 

91 content = _renderer(str(tokens[idx].content)) 

92 return f'<div class="math amsmath">\n{content}\n</div>\n' 

93 

94 md.add_render_rule("amsmath", render_amsmath_block) 

95 

96 

97def match_environment(string: str) -> None | tuple[str, str, int]: 

98 match_open = RE_OPEN.match(string) 

99 if not match_open: 

100 return None 

101 environment = match_open.group(1) 

102 numbered = match_open.group(2) 

103 match_close = re.search( 

104 r"\\end\{" + environment + numbered.replace("*", r"\*") + "\\}", string 

105 ) 

106 if not match_close: 

107 return None 

108 return (environment, numbered, match_close.end()) 

109 

110 

111def amsmath_block( 

112 state: StateBlock, startLine: int, endLine: int, silent: bool 

113) -> bool: 

114 if is_code_block(state, startLine): 

115 return False 

116 

117 begin = state.bMarks[startLine] + state.tShift[startLine] 

118 

119 outcome = match_environment(state.src[begin:]) 

120 if not outcome: 

121 return False 

122 environment, numbered, endpos = outcome 

123 endpos += begin 

124 

125 line = startLine 

126 while line < endLine: 

127 if endpos >= state.bMarks[line] and endpos <= state.eMarks[line]: 

128 # line for end of block math found ... 

129 state.line = line + 1 

130 break 

131 line += 1 

132 

133 if not silent: 

134 token = state.push("amsmath", "math", 0) 

135 token.block = True 

136 token.content = state.src[begin:endpos] 

137 token.meta = {"environment": environment, "numbered": numbered} 

138 token.map = [startLine, line] 

139 

140 return True