Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mistune/plugins/footnotes.py: 31%

75 statements  

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

1import re 

2from ..core import BlockState 

3from ..util import unikey 

4from ..helpers import LINK_LABEL 

5 

6__all__ = ['footnotes'] 

7 

8_PARAGRAPH_SPLIT = re.compile(r'\n{2,}') 

9# https://michelf.ca/projects/php-markdown/extra/#footnotes 

10REF_FOOTNOTE = ( 

11 r'^(?P<footnote_lead> {0,3})' 

12 r'\[\^(?P<footnote_key>' + LINK_LABEL + r')]:[ \t]' 

13 r'(?P<footnote_text>[^\n]*(?:\n+|$)' 

14 r'(?:(?P=footnote_lead) {1,3}(?! )[^\n]*\n+)*' 

15 r')' 

16) 

17 

18INLINE_FOOTNOTE = r'\[\^(?P<footnote_key>' + LINK_LABEL + r')\]' 

19 

20 

21def parse_inline_footnote(inline, m: re.Match, state): 

22 key = unikey(m.group('footnote_key')) 

23 ref = state.env.get('ref_footnotes') 

24 if ref and key in ref: 

25 notes = state.env.get('footnotes') 

26 if not notes: 

27 notes = [] 

28 if key not in notes: 

29 notes.append(key) 

30 state.env['footnotes'] = notes 

31 state.append_token({ 

32 'type': 'footnote_ref', 

33 'raw': key, 

34 'attrs': {'index': notes.index(key) + 1} 

35 }) 

36 else: 

37 state.append_token({'type': 'text', 'raw': m.group(0)}) 

38 return m.end() 

39 

40 

41def parse_ref_footnote(block, m: re.Match, state: BlockState): 

42 ref = state.env.get('ref_footnotes') 

43 if not ref: 

44 ref = {} 

45 

46 key = unikey(m.group('footnote_key')) 

47 if key not in ref: 

48 ref[key] = m.group('footnote_text') 

49 state.env['ref_footnotes'] = ref 

50 return m.end() 

51 

52 

53def parse_footnote_item(block, key: str, index: int, state: BlockState): 

54 ref = state.env.get('ref_footnotes') 

55 text = ref[key] 

56 

57 lines = text.splitlines() 

58 second_line = None 

59 for second_line in lines[1:]: 

60 if second_line: 

61 break 

62 

63 if second_line: 

64 spaces = len(second_line) - len(second_line.lstrip()) 

65 pattern = re.compile(r'^ {' + str(spaces) + r',}', flags=re.M) 

66 text = pattern.sub('', text).strip() 

67 items = _PARAGRAPH_SPLIT.split(text) 

68 children = [{'type': 'paragraph', 'text': s} for s in items] 

69 else: 

70 text = text.strip() 

71 children = [{'type': 'paragraph', 'text': text}] 

72 return { 

73 'type': 'footnote_item', 

74 'children': children, 

75 'attrs': {'key': key, 'index': index} 

76 } 

77 

78 

79def md_footnotes_hook(md, result: str, state: BlockState): 

80 notes = state.env.get('footnotes') 

81 if not notes: 

82 return result 

83 

84 children = [ 

85 parse_footnote_item(md.block, k, i + 1, state) 

86 for i, k in enumerate(notes) 

87 ] 

88 state = BlockState() 

89 state.tokens = [{'type': 'footnotes', 'children': children}] 

90 output = md.render_state(state) 

91 return result + output 

92 

93 

94def render_footnote_ref(renderer, key: str, index: int): 

95 i = str(index) 

96 html = '<sup class="footnote-ref" id="fnref-' + i + '">' 

97 return html + '<a href="#fn-' + i + '">' + i + '</a></sup>' 

98 

99 

100def render_footnotes(renderer, text: str): 

101 return '<section class="footnotes">\n<ol>\n' + text + '</ol>\n</section>\n' 

102 

103 

104def render_footnote_item(renderer, text: str, key: str, index: int): 

105 i = str(index) 

106 back = '<a href="#fnref-' + i + '" class="footnote">&#8617;</a>' 

107 text = text.rstrip()[:-4] + back + '</p>' 

108 return '<li id="fn-' + i + '">' + text + '</li>\n' 

109 

110 

111def footnotes(md): 

112 """A mistune plugin to support footnotes, spec defined at 

113 https://michelf.ca/projects/php-markdown/extra/#footnotes 

114 

115 Here is an example: 

116 

117 .. code-block:: text 

118 

119 That's some text with a footnote.[^1] 

120 

121 [^1]: And that's the footnote. 

122 

123 It will be converted into HTML: 

124 

125 .. code-block:: html 

126 

127 <p>That's some text with a footnote.<sup class="footnote-ref" id="fnref-1"><a href="#fn-1">1</a></sup></p> 

128 <section class="footnotes"> 

129 <ol> 

130 <li id="fn-1"><p>And that's the footnote.<a href="#fnref-1" class="footnote">&#8617;</a></p></li> 

131 </ol> 

132 </section> 

133 

134 :param md: Markdown instance 

135 """ 

136 md.inline.register( 

137 'footnote', 

138 INLINE_FOOTNOTE, 

139 parse_inline_footnote, 

140 before='link', 

141 ) 

142 md.block.register( 

143 'ref_footnote', 

144 REF_FOOTNOTE, 

145 parse_ref_footnote, 

146 before='ref_link', 

147 ) 

148 md.after_render_hooks.append(md_footnotes_hook) 

149 

150 if md.renderer and md.renderer.NAME == 'html': 

151 md.renderer.register('footnote_ref', render_footnote_ref) 

152 md.renderer.register('footnote_item', render_footnote_item) 

153 md.renderer.register('footnotes', render_footnotes)