Coverage for C:\leo.repo\leo-editor\leo\plugins\importers\markdown.py : 88%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#@+leo-ver=5-thin
2#@+node:ekr.20140725190808.18066: * @file ../plugins/importers/markdown.py
3"""The @auto importer for the markdown language."""
4import re
5from leo.core import leoGlobals as g
6from leo.plugins.importers import linescanner
7Importer = linescanner.Importer
8#@+others
9#@+node:ekr.20161124192050.2: ** class Markdown_Importer
10class Markdown_Importer(Importer):
11 """The importer for the markdown lanuage."""
13 def __init__(self, importCommands, **kwargs):
14 """Markdown_Importer.__init__"""
15 super().__init__(importCommands,
16 language='md',
17 state_class=None,
18 strict=False,
19 )
20 self.underline_dict = {}
22 #@+others
23 #@+node:ekr.20161124193148.1: *3* md_i.gen_lines & helpers
24 def gen_lines(self, lines, parent):
25 """Node generator for markdown importer."""
26 if all(s.isspace() for s in lines):
27 return
28 self.vnode_info = {
29 # Keys are vnodes, values are inner dicts.
30 parent.v: {
31 'lines': [],
32 }
33 }
34 # We may as well do this first. See warning below.
35 self.stack = [parent]
36 in_code = False
38 skip = 0
39 for i, line in enumerate(lines):
40 top = self.stack[-1]
41 level, name = self.is_hash(line)
42 if skip > 0:
43 skip -= 1
44 elif not in_code and self.lookahead_underline(i, lines):
45 level = 1 if lines[i + 1].startswith('=') else 2
46 self.make_node(level, line)
47 skip = 1
48 elif not in_code and name:
49 self.make_node(level, name)
50 elif i == 0:
51 self.make_decls_node(line)
52 elif in_code:
53 if line.startswith("```"):
54 in_code = False
55 self.add_line(top, line)
56 elif line.startswith("```"):
57 in_code = True
58 self.add_line(top, line)
59 else:
60 self.add_line(top, line)
61 #@+node:ekr.20161124193148.2: *4* md_i.find_parent
62 def find_parent(self, level, h):
63 """
64 Return the parent at the indicated level, allocating
65 place-holder nodes as necessary.
66 """
67 assert level >= 0
68 while level < len(self.stack):
69 self.stack.pop()
70 top = self.stack[-1]
71 if 1: # Experimental fix for #877.
72 if level > len(self.stack):
73 print('')
74 g.trace('Unexpected markdown level for: %s' % h)
75 print('')
76 while level > len(self.stack):
77 child = self.create_child_node(
78 parent=top,
79 line=None,
80 headline='INSERTED NODE'
81 )
82 self.stack.append(child)
83 assert level == len(self.stack), (level, len(self.stack))
84 child = self.create_child_node(
85 parent=top,
86 line=None,
87 headline=h, # Leave the headline alone
88 )
89 self.stack.append(child)
90 assert self.stack
91 assert 0 <= level < len(self.stack), (level, len(self.stack))
92 return self.stack[level]
93 #@+node:ekr.20161202090722.1: *4* md_i.is_hash
94 # Allow any non-blank after the hashes.
95 md_hash_pattern = re.compile(r'^(#+)\s*(.+)\s*\n')
97 def is_hash(self, line):
98 """
99 Return level, name if line is a hash section line.
100 else return None, None.
101 """
102 m = self.md_hash_pattern.match(line)
103 if m:
104 level = len(m.group(1))
105 # name = m.group(2) + m.group(3)
106 name = m.group(2).strip()
107 if name:
108 return level, name
109 return None, None
110 #@+node:ekr.20161202085119.1: *4* md_i.is_underline
111 md_pattern_table = (
112 re.compile(r'^(=+)\n'),
113 re.compile(r'^(-+)\n'),
114 )
116 def is_underline(self, line):
117 """True if line is all '-' or '=' characters."""
119 for pattern in self.md_pattern_table:
120 m = pattern.match(line)
121 if m and len(m.group(1)) >= 4:
122 return True
123 return False
124 #@+node:ekr.20161202085032.1: *4* md_i.lookahead_underline
125 def lookahead_underline(self, i, lines):
126 """True if lines[i:i+1] form an underlined line."""
127 if i + 1 < len(lines):
128 line0 = lines[i]
129 line1 = lines[i + 1]
130 ch0 = self.is_underline(line0)
131 ch1 = self.is_underline(line1)
132 return not ch0 and not line0.isspace() and ch1 and len(line1) >= 4
133 return False
134 #@+node:ekr.20161125113240.1: *4* md_i.make_decls_node
135 def make_decls_node(self, line):
136 """Make a decls node."""
137 parent = self.stack[-1]
138 assert parent == self.root, repr(parent)
139 child = self.create_child_node(
140 parent=self.stack[-1],
141 line=line,
142 headline='!Declarations',
143 )
144 self.stack.append(child)
145 #@+node:ekr.20161125095217.1: *4* md_i.make_node
146 def make_node(self, level, name):
147 """Create a new node."""
148 self.find_parent(level=level, h=name)
149 #@+node:ekr.20161125225349.1: *3* md_i.post_pass
150 def post_pass(self, parent):
151 """A do-nothing post-pass for markdown."""
152 #@+node:ekr.20161202074507.1: *3* md_i.check
153 def check(self, unused_s, parent):
154 """
155 A do-nothing perfect-import check for markdown.
156 We don't want to prevent writer.markdown from converting
157 all headlines to hashed sections.
158 """
159 return True
160 #@-others
161#@-others
162importer_dict = {
163 '@auto': ['@auto-md', '@auto-markdown',],
164 'func': Markdown_Importer.do_import(),
165 'extensions': ['.md', '.rmd', '.Rmd',],
166}
167#@@language python
168#@@tabwidth -4
169#@-leo