2
2
Preprocessor that transforms markdown cells: Insert numbering in from of heading
3
3
"""
4
4
5
- import re
5
+ from traitlets . log import get_logger
6
6
7
7
from nbconvert .preprocessors .base import Preprocessor
8
8
9
+ logger = get_logger ()
10
+
11
+ try : # for Mistune >= 3.0
12
+ import mistune
13
+ from mistune .core import BlockState
14
+ from mistune .renderers .markdown import MarkdownRenderer
15
+ except ImportError : # for Mistune >= 2.0
16
+ logger .error ("NumberedHeadingsPreprocessor requires mistune >= 3" )
17
+
9
18
10
19
class NumberedHeadingsPreprocessor (Preprocessor ):
11
20
"""Pre-processor that will rewrite markdown headings to include numberings."""
12
21
13
22
def __init__ (self , * args , ** kwargs ):
14
23
"""Init"""
15
24
super ().__init__ (* args , ** kwargs )
25
+ self .md_parser = mistune .create_markdown (renderer = None )
26
+ self .md_renderer = MarkdownRenderer ()
16
27
self .current_numbering = [0 ]
17
28
18
29
def format_numbering (self ):
@@ -29,23 +40,24 @@ def _inc_current_numbering(self, level):
29
40
self .current_numbering = self .current_numbering [:level ]
30
41
self .current_numbering [level - 1 ] += 1
31
42
32
- def _transform_markdown_line (self , line , resources ):
33
- """Rewrites one markdown line, if needed"""
34
- if m := re .match (r"^(?P<level>#+) (?P<heading>.*)" , line ):
35
- level = len (m .group ("level" ))
36
- self ._inc_current_numbering (level )
37
- old_heading = m .group ("heading" ).strip ()
38
- new_heading = self .format_numbering () + " " + old_heading
39
- return "#" * level + " " + new_heading
40
-
41
- return line
42
-
43
43
def preprocess_cell (self , cell , resources , index ):
44
44
"""Rewrites all the headings in the cell if it is markdown"""
45
- if cell ["cell_type" ] == "markdown" :
46
- cell ["source" ] = "\n " .join (
47
- self ._transform_markdown_line (line , resources )
48
- for line in cell ["source" ].splitlines ()
49
- )
50
-
51
- return cell , resources
45
+ if cell ["cell_type" ] != "markdown" :
46
+ return cell , resources
47
+ try :
48
+ md_ast = self .md_parser (cell ["source" ])
49
+ assert not isinstance (md_ast , str ) # type guard ; str is not returned by ast parser
50
+ for element in md_ast :
51
+ if element ["type" ] == "heading" :
52
+ level = element ["attrs" ]["level" ]
53
+ self ._inc_current_numbering (level )
54
+ if len (element ["children" ]) > 0 :
55
+ child = element ["children" ][0 ]
56
+ if child ["type" ] == "text" :
57
+ child ["raw" ] = self .format_numbering () + " " + child ["raw" ]
58
+ new_source = self .md_renderer (md_ast , BlockState ())
59
+ cell ["source" ] = new_source
60
+ return cell , resources
61
+ except Exception :
62
+ logger .warning ("Failed processing cell headings" , exc_info = True )
63
+ return cell , resources
0 commit comments