1
1
from __future__ import absolute_import , unicode_literals
2
2
3
3
import re
4
- from importlib import import_module
5
4
from commonmark import common
6
5
from commonmark .common import unescape_string
7
6
from commonmark .inlines import InlineParser
8
7
from commonmark .node import Node
9
- from commonmark .utils import to_camel_case
10
8
11
9
12
10
CODE_INDENT = 4
21
19
r'^<[/]?(?:address|article|aside|base|basefont|blockquote|body|'
22
20
r'caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|'
23
21
r'fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|'
24
- r'header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta| '
22
+ r'header|hr|html|iframe|legend|li|link|main|menu|menuitem|'
25
23
r'nav|noframes|ol|optgroup|option|p|param|section|source|title|'
26
24
r'summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)'
27
25
r'(?:\s|[/]?[>]|$)' ,
45
43
reBulletListMarker = re .compile (r'^[*+-]' )
46
44
reOrderedListMarker = re .compile (r'^(\d{1,9})([.)])' )
47
45
reATXHeadingMarker = re .compile (r'^#{1,6}(?:[ \t]+|$)' )
48
- reCodeFence = re .compile (r'^`{3,}(?!.*`)|^~{3,}(?!.*~) ' )
46
+ reCodeFence = re .compile (r'^`{3,}(?!.*`)|^~{3,}' )
49
47
reClosingCodeFence = re .compile (r'^(?:`{3,}|~{3,})(?= *$)' )
50
48
reSetextHeadingLine = re .compile (r'^(?:=+|-+)[ \t]*$' )
51
49
reLineEnding = re .compile (r'\r\n|\n|\r' )
@@ -57,7 +55,7 @@ def is_blank(s):
57
55
58
56
59
57
def is_space_or_tab (s ):
60
- return s == ' ' or s == '\t '
58
+ return s in ( ' ' , '\t ' )
61
59
62
60
63
61
def peek (ln , pos ):
@@ -73,9 +71,12 @@ def ends_with_blank_line(block):
73
71
while block :
74
72
if block .last_line_blank :
75
73
return True
76
- if (block .t == 'list' or block .t == 'item' ):
74
+ if not block .last_line_checked and \
75
+ block .t in ('list' , 'item' ):
76
+ block .last_line_checked = True
77
77
block = block .last_child
78
78
else :
79
+ block .last_line_checked = True
79
80
break
80
81
81
82
return False
@@ -94,6 +95,8 @@ def parse_list_marker(parser, container):
94
95
'padding' : None ,
95
96
'marker_offset' : parser .indent ,
96
97
}
98
+ if parser .indent >= 4 :
99
+ return None
97
100
m = re .search (reBulletListMarker , rest )
98
101
m2 = re .search (reOrderedListMarker , rest )
99
102
if m :
@@ -515,15 +518,25 @@ def setext_heading(parser, container=None):
515
518
parser .current_line [parser .next_nonspace :])
516
519
if m :
517
520
parser .close_unmatched_blocks ()
518
- heading = Node ('heading' , container .sourcepos )
519
- heading .level = 1 if m .group ()[0 ] == '=' else 2
520
- heading .string_content = container .string_content
521
- container .insert_after (heading )
522
- container .unlink ()
523
- parser .tip = heading
524
- parser .advance_offset (
525
- len (parser .current_line ) - parser .offset , False )
526
- return 2
521
+ # resolve reference link definitiosn
522
+ while peek (container .string_content , 0 ) == '[' :
523
+ pos = parser .inline_parser .parseReference (
524
+ container .string_content , parser .refmap )
525
+ if not pos :
526
+ break
527
+ container .string_content = container .string_content [pos :]
528
+ if container .string_content :
529
+ heading = Node ('heading' , container .sourcepos )
530
+ heading .level = 1 if m .group ()[0 ] == '=' else 2
531
+ heading .string_content = container .string_content
532
+ container .insert_after (heading )
533
+ container .unlink ()
534
+ parser .tip = heading
535
+ parser .advance_offset (
536
+ len (parser .current_line ) - parser .offset , False )
537
+ return 2
538
+ else :
539
+ return 0
527
540
528
541
return 0
529
542
@@ -610,13 +623,8 @@ def add_child(self, tag, offset):
610
623
""" Add block of type tag as a child of the tip. If the tip can't
611
624
accept children, close and finalize it and try its parent,
612
625
and so on til we find a block that can accept children."""
613
- block_class = getattr (import_module ('commonmark.blocks' ),
614
- to_camel_case (self .tip .t ))
615
- while not block_class .can_contain (tag ):
626
+ while not self .blocks [self .tip .t ].can_contain (tag ):
616
627
self .finalize (self .tip , self .line_number - 1 )
617
- block_class = getattr (
618
- import_module ('commonmark.blocks' ),
619
- to_camel_case (self .tip .t ))
620
628
621
629
column_number = offset + 1
622
630
new_block = Node (tag , [[self .line_number , column_number ], [0 , 0 ]])
@@ -725,15 +733,15 @@ def incorporate_line(self, ln):
725
733
# For each containing block, try to parse the associated line start.
726
734
# Bail out on failure: container will point to the last matching block.
727
735
# Set all_matched to false if not all containers match.
728
- last_child = container .last_child
729
- while last_child and last_child .is_open :
736
+ while True :
737
+ last_child = container .last_child
738
+ if not (last_child and last_child .is_open ):
739
+ break
730
740
container = last_child
731
741
732
742
self .find_next_nonspace ()
733
- block_class = getattr (
734
- import_module ('commonmark.blocks' ),
735
- to_camel_case (container .t ))
736
- rv = block_class .continue_ (self , container )
743
+
744
+ rv = self .blocks [container .t ].continue_ (self , container )
737
745
if rv == 0 :
738
746
# we've matched, keep going
739
747
pass
@@ -745,21 +753,19 @@ def incorporate_line(self, ln):
745
753
self .last_line_length = len (ln )
746
754
return
747
755
else :
748
- raise ValueError ('returned illegal value, must be 0, 1, or 2' )
756
+ raise ValueError (
757
+ 'continue_ returned illegal value, must be 0, 1, or 2' )
749
758
750
759
if not all_matched :
751
760
# back up to last matching block
752
761
container = container .parent
753
762
break
754
763
755
- last_child = container .last_child
756
-
757
764
self .all_closed = (container == self .oldtip )
758
765
self .last_matched_container = container
759
766
760
- block_class = getattr (import_module ('commonmark.blocks' ),
761
- to_camel_case (container .t ))
762
- matched_leaf = container .t != 'paragraph' and block_class .accepts_lines
767
+ matched_leaf = container .t != 'paragraph' and \
768
+ self .blocks [container .t ].accepts_lines
763
769
starts = self .block_starts
764
770
starts_len = len (starts .METHODS )
765
771
# Unless last matched container is a code block, try new container
@@ -824,9 +830,7 @@ def incorporate_line(self, ln):
824
830
cont .last_line_blank = last_line_blank
825
831
cont = cont .parent
826
832
827
- block_class = getattr (import_module ('commonmark.blocks' ),
828
- to_camel_case (t ))
829
- if block_class .accepts_lines :
833
+ if self .blocks [t ].accepts_lines :
830
834
self .add_line ()
831
835
# if HtmlBlock, check for end condition
832
836
if t == 'html_block' and \
@@ -853,9 +857,8 @@ def finalize(self, block, line_number):
853
857
above = block .parent
854
858
block .is_open = False
855
859
block .sourcepos [1 ] = [line_number , self .last_line_length ]
856
- block_class = getattr (import_module ('commonmark.blocks' ),
857
- to_camel_case (block .t ))
858
- block_class .finalize (self , block )
860
+
861
+ self .blocks [block .t ].finalize (self , block )
859
862
860
863
self .tip = above
861
864
@@ -897,3 +900,9 @@ def parse(self, my_input):
897
900
self .finalize (self .tip , length )
898
901
self .process_inlines (self .doc )
899
902
return self .doc
903
+
904
+
905
+ CAMEL_RE = re .compile ("(.)([A-Z](?:[a-z]+|(?<=[a-z0-9].)))" )
906
+ Parser .blocks = dict (
907
+ (CAMEL_RE .sub (r'\1_\2' , cls .__name__ ).lower (), cls )
908
+ for cls in Block .__subclasses__ ())
0 commit comments