Coverage for C:\Repos\leo-editor\leo\plugins\importers\c.py: 90%
134 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
1#@+leo-ver=5-thin
2#@+node:ekr.20140723122936.17926: * @file ../plugins/importers/c.py
3"""The @auto importer for the C language and other related languages."""
4import re
5from leo.core import leoGlobals as g
6from leo.plugins.importers import linescanner
7assert g
8Importer = linescanner.Importer
9Target = linescanner.Target
10#@+others
11#@+node:ekr.20140723122936.17928: ** class C_Importer
12class C_Importer(Importer):
14 #@+others
15 #@+node:ekr.20200819144754.1: *3* c_i.ctor
16 def __init__(self, importCommands, **kwargs):
17 """C_Importer.__init__"""
18 # Init the base class.
19 super().__init__(
20 importCommands,
21 language='c',
22 state_class=C_ScanState,
23 )
24 self.headline = None
25 # Fix #545 by supporting @data c_import_typedefs.
26 self.type_keywords = [
27 'auto', 'bool', 'char', 'const', 'double',
28 'extern', 'float', 'int', 'register',
29 'signed', 'short', 'static', 'typedef',
30 'union', 'unsigned', 'void', 'volatile',
31 ]
32 aSet = set(
33 self.type_keywords +
34 (self.c.config.getData('c_import_typedefs') or [])
35 )
36 self.c_type_names = '(%s)' % '|'.join(list(aSet))
37 self.c_types_pattern = re.compile(self.c_type_names)
38 self.c_class_pattern = re.compile(r'\s*(%s\s*)*\s*class\s+(\w+)' % (self.c_type_names))
39 self.c_func_pattern = re.compile(r'\s*(%s\s*)+\s*([\w:]+)' % (self.c_type_names))
40 self.c_keywords = '(%s)' % '|'.join([
41 'break', 'case', 'continue', 'default', 'do', 'else', 'enum',
42 'for', 'goto', 'if', 'return', 'sizeof', 'struct', 'switch', 'while',
43 ])
44 self.c_keywords_pattern = re.compile(self.c_keywords)
45 #@+node:ekr.20200819073508.1: *3* c_i.clean_headline
46 def clean_headline(self, s, p=None):
47 """
48 Adjust headline for templates.
49 """
50 if not p:
51 return s.strip()
52 lines = self.get_lines(p)
53 if s.startswith('template') and len(lines) > 1:
54 line = lines[1]
55 # Filter out all keywords and cruft.
56 # This isn't perfect, but it's a good start.
57 for z in self.type_keywords:
58 line = re.sub(fr"\b{z}\b", '', line)
59 for ch in '()[]{}=':
60 line = line.replace(ch, '')
61 return line.strip()
62 return s.strip()
63 #@+node:ekr.20161204173153.1: *3* c_i.match_name_patterns
64 c_name_pattern = re.compile(r'\s*([\w:]+)')
66 def match_name_patterns(self, line):
67 """Set self.headline if the line defines a typedef name."""
68 m = self.c_name_pattern.match(line)
69 if m:
70 word = m.group(1)
71 if not self.c_types_pattern.match(word):
72 self.headline = word
73 #@+node:ekr.20161204165700.1: *3* c_i.match_start_patterns
74 # Define patterns that can start a block
75 c_extern_pattern = re.compile(r'\s*extern\s+(\"\w+\")')
76 c_template_pattern = re.compile(r'\s*template\s*<(.*?)>\s*$')
77 c_typedef_pattern = re.compile(r'\s*(\w+)\s*\*\s*$')
79 def match_start_patterns(self, line):
80 """
81 True if line matches any block-starting pattern.
82 If true, set self.headline.
83 """
84 m = self.c_extern_pattern.match(line)
85 if m:
86 self.headline = line.strip()
87 return True
88 # #1626
89 m = self.c_template_pattern.match(line)
90 if m:
91 self.headline = line.strip()
92 return True
93 m = self.c_class_pattern.match(line)
94 if m:
95 prefix = m.group(1).strip() if m.group(1) else ''
96 self.headline = '%sclass %s' % (prefix, m.group(3))
97 self.headline = self.headline.strip()
98 return True
99 m = self.c_func_pattern.match(line)
100 if m:
101 if self.c_types_pattern.match(m.group(3)):
102 return True
103 prefix = m.group(1).strip() if m.group(1) else ''
104 self.headline = '%s %s' % (prefix, m.group(3))
105 self.headline = self.headline.strip()
106 return True
107 m = self.c_typedef_pattern.match(line)
108 if m:
109 # Does not set self.headline.
110 return True
111 m = self.c_types_pattern.match(line)
112 return bool(m)
113 #@+node:ekr.20161204072326.1: *3* c_i.start_new_block
114 def start_new_block(self, i, lines, new_state, prev_state, stack):
115 """Create a child node and update the stack."""
116 line = lines[i]
117 target = stack[-1]
118 # Insert the reference in *this* node.
119 h = self.gen_ref(line, target.p, target)
120 # Create a new child and associated target.
121 if self.headline:
122 h = self.headline
123 if new_state.level() > prev_state.level():
124 child = self.create_child_node(target.p, line, h)
125 else:
126 # We may not have seen the { yet, so adjust.
127 # Without this, the new block becomes a child of the preceding.
128 new_state = C_ScanState()
129 new_state.curlies = prev_state.curlies + 1
130 child = self.create_child_node(target.p, line, h)
131 stack.append(Target(child, new_state))
132 # Add all additional lines of the signature.
133 skip = self.skip # Don't change the ivar!
134 while skip > 0:
135 skip -= 1
136 i += 1
137 assert i < len(lines), (i, len(lines))
138 line = lines[i]
139 if not self.headline:
140 self.match_name_patterns(line)
141 if self.headline:
142 child.h = '%s %s' % (child.h.strip(), self.headline)
143 self.add_line(child, lines[i])
144 #@+node:ekr.20161204155335.1: *3* c_i.starts_block
145 def starts_block(self, i, lines, new_state, prev_state):
146 """True if the new state starts a block."""
147 self.headline = None
148 line = lines[i]
149 if prev_state.context:
150 return False
151 if self.c_keywords_pattern.match(line):
152 return False
153 if not self.match_start_patterns(line):
154 return False
155 # Must not be a complete statement.
156 if line.find(';') > -1:
157 return False
158 # Scan ahead until an open { is seen. the skip count.
159 self.skip = 0
160 while self.skip < 10:
161 if new_state.level() > prev_state.level():
162 return True
163 self.skip += 1
164 i += 1
165 if i < len(lines):
166 line = lines[i]
167 prev_state = new_state
168 new_state = self.scan_line(line, prev_state)
169 else:
170 break
171 return False
172 #@-others
173#@+node:ekr.20161108223159.1: ** class C_ScanState
174class C_ScanState:
175 """A class representing the state of the C line-oriented scan."""
177 def __init__(self, d=None):
178 """C_ScanSate ctor"""
179 if d:
180 prev = d.get('prev')
181 self.context = prev.context
182 self.curlies = prev.curlies
183 else:
184 self.context = ''
185 self.curlies = 0
187 def __repr__(self):
188 """C_ScanState.__repr__"""
189 return 'C_ScanState context: %r curlies: %s' % (self.context, self.curlies)
191 __str__ = __repr__
193 #@+others
194 #@+node:ekr.20161119115315.1: *3* c_state.level
195 def level(self):
196 """C_ScanState.level."""
197 return self.curlies
198 #@+node:ekr.20161118051111.1: *3* c_state.update
199 def update(self, data):
200 """
201 Update the state using the 6-tuple returned by i.scan_line.
202 Return i = data[1]
203 """
204 context, i, delta_c, delta_p, delta_s, bs_nl = data
205 # self.bs_nl = bs_nl
206 self.context = context
207 self.curlies += delta_c
208 return i
210 #@-others
212#@-others
213importer_dict = {
214 'func': C_Importer.do_import(),
215 'extensions': ['.c', '.cc', '.c++', '.cpp', '.cxx', '.h', '.h++',],
216}
217#@@language python
218#@@tabwidth -4
219#@-leo