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

82 statements  

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

1import re 

2from ..inline_parser import LINK_LABEL 

3from ..util import unikey 

4 

5__all__ = ['plugin_footnotes'] 

6 

7#: inline footnote syntax looks like:: 

8#: 

9#: [^key] 

10INLINE_FOOTNOTE_PATTERN = r'\[\^(' + LINK_LABEL + r')\]' 

11 

12#: define a footnote item like:: 

13#: 

14#: [^key]: paragraph text to describe the note 

15DEF_FOOTNOTE = re.compile( 

16 r'( {0,3})\[\^(' + LINK_LABEL + r')\]:[ \t]*(' 

17 r'[^\n]*\n+' 

18 r'(?:\1 {1,3}(?! )[^\n]*\n+)*' 

19 r')' 

20) 

21 

22 

23def parse_inline_footnote(inline, m, state): 

24 key = unikey(m.group(1)) 

25 def_footnotes = state.get('def_footnotes') 

26 if not def_footnotes or key not in def_footnotes: 

27 return 'text', m.group(0) 

28 

29 index = state.get('footnote_index', 0) 

30 index += 1 

31 state['footnote_index'] = index 

32 state['footnotes'].append(key) 

33 return 'footnote_ref', key, index 

34 

35 

36def parse_def_footnote(block, m, state): 

37 key = unikey(m.group(2)) 

38 if key not in state['def_footnotes']: 

39 state['def_footnotes'][key] = m.group(3) 

40 

41 

42def parse_footnote_item(block, k, i, state): 

43 def_footnotes = state['def_footnotes'] 

44 text = def_footnotes[k] 

45 

46 stripped_text = text.strip() 

47 if '\n' not in stripped_text: 

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

49 else: 

50 lines = text.splitlines() 

51 for second_line in lines[1:]: 

52 if second_line: 

53 break 

54 

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

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

57 text = pattern.sub('', text) 

58 children = block.parse_text(text, state) 

59 if not isinstance(children, list): 

60 children = [children] 

61 

62 return { 

63 'type': 'footnote_item', 

64 'children': children, 

65 'params': (k, i) 

66 } 

67 

68 

69def md_footnotes_hook(md, result, state): 

70 footnotes = state.get('footnotes') 

71 if not footnotes: 

72 return result 

73 

74 children = [ 

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

76 for i, k in enumerate(footnotes) 

77 ] 

78 tokens = [{'type': 'footnotes', 'children': children}] 

79 output = md.block.render(tokens, md.inline, state) 

80 return result + output 

81 

82 

83def render_ast_footnote_ref(key, index): 

84 return {'type': 'footnote_ref', 'key': key, 'index': index} 

85 

86 

87def render_ast_footnote_item(children, key, index): 

88 return { 

89 'type': 'footnote_item', 

90 'children': children, 

91 'key': key, 

92 'index': index, 

93 } 

94 

95 

96def render_html_footnote_ref(key, index): 

97 i = str(index) 

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

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

100 

101 

102def render_html_footnotes(text): 

103 return ( 

104 '<section class="footnotes">\n<ol>\n' 

105 + text + 

106 '</ol>\n</section>\n' 

107 ) 

108 

109 

110def render_html_footnote_item(text, key, index): 

111 i = str(index) 

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

113 

114 text = text.rstrip() 

115 if text.endswith('</p>'): 

116 text = text[:-4] + back + '</p>' 

117 else: 

118 text = text + back 

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

120 

121 

122def plugin_footnotes(md): 

123 md.inline.register_rule( 

124 'footnote', 

125 INLINE_FOOTNOTE_PATTERN, 

126 parse_inline_footnote 

127 ) 

128 index = md.inline.rules.index('std_link') 

129 if index != -1: 

130 md.inline.rules.insert(index, 'footnote') 

131 else: 

132 md.inline.rules.append('footnote') 

133 

134 md.block.register_rule('def_footnote', DEF_FOOTNOTE, parse_def_footnote) 

135 index = md.block.rules.index('def_link') 

136 if index != -1: 

137 md.block.rules.insert(index, 'def_footnote') 

138 else: 

139 md.block.rules.append('def_footnote') 

140 

141 if md.renderer.NAME == 'html': 

142 md.renderer.register('footnote_ref', render_html_footnote_ref) 

143 md.renderer.register('footnote_item', render_html_footnote_item) 

144 md.renderer.register('footnotes', render_html_footnotes) 

145 elif md.renderer.NAME == 'ast': 

146 md.renderer.register('footnote_ref', render_ast_footnote_ref) 

147 md.renderer.register('footnote_item', render_ast_footnote_item) 

148 

149 md.after_render_hooks.append(md_footnotes_hook)