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
+
16
+ MISTUNE_V3 = True
17
+ except ImportError : # for Mistune >= 2.0
18
+ MISTUNE_V3 = False
19
+
20
+ WRONG_MISTUNE_VERSION_ERROR = "Error: NumberedHeadingsPreprocessor requires mistune >= 3"
21
+
9
22
10
23
class NumberedHeadingsPreprocessor (Preprocessor ):
11
24
"""Pre-processor that will rewrite markdown headings to include numberings."""
12
25
13
26
def __init__ (self , * args , ** kwargs ):
14
27
"""Init"""
15
28
super ().__init__ (* args , ** kwargs )
29
+ if not MISTUNE_V3 :
30
+ raise Exception (WRONG_MISTUNE_VERSION_ERROR )
31
+ self .md_parser = mistune .create_markdown (renderer = None )
32
+ self .md_renderer = MarkdownRenderer ()
16
33
self .current_numbering = [0 ]
17
34
18
35
def format_numbering (self ):
@@ -29,23 +46,24 @@ def _inc_current_numbering(self, level):
29
46
self .current_numbering = self .current_numbering [:level ]
30
47
self .current_numbering [level - 1 ] += 1
31
48
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
49
def preprocess_cell (self , cell , resources , index ):
44
50
"""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
51
+ if cell ["cell_type" ] != "markdown" :
52
+ return cell , resources
53
+ try :
54
+ md_ast = self .md_parser (cell ["source" ])
55
+ assert not isinstance (md_ast , str ) # type guard ; str is not returned by ast parser
56
+ for element in md_ast :
57
+ if element ["type" ] == "heading" :
58
+ level = element ["attrs" ]["level" ]
59
+ self ._inc_current_numbering (level )
60
+ if len (element ["children" ]) > 0 :
61
+ child = element ["children" ][0 ]
62
+ if child ["type" ] == "text" :
63
+ child ["raw" ] = self .format_numbering () + " " + child ["raw" ]
64
+ new_source = self .md_renderer (md_ast , BlockState ())
65
+ cell ["source" ] = new_source
66
+ return cell , resources
67
+ except Exception :
68
+ logger .warning ("Failed processing cell headings" , exc_info = True )
69
+ return cell , resources
0 commit comments