Coverage for C:\Repos\leo-editor\leo\plugins\importers\typescript.py: 65%
112 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.18152: * @file ../plugins/importers/typescript.py
3"""The @auto importer for TypeScript."""
4import re
5from leo.core import leoGlobals as g
6from leo.plugins.importers import linescanner
7assert g
8Importer = linescanner.Importer
9#@+others
10#@+node:ekr.20161118093751.1: ** class TS_Importer(Importer)
11class TS_Importer(Importer):
13 #@+<< define non-function patterns >>
14 #@+node:ekr.20200817090227.1: *3* << define non-function patterns >>
15 non_function_patterns = (
17 re.compile(r'catch\s*\(.*\)'),
18 )
19 #@-<< define non-function patterns >>
20 #@+<< define function patterns >>
21 #@+node:ekr.20180523172655.1: *3* << define function patterns >>
22 kinds = r'(async|public|private|static)'
23 #
24 # The pattern table. Order matters!
25 function_patterns = (
26 (1, re.compile(r'(interface\s+\w+)')), # interface name
27 (1, re.compile(r'(class\s+\w+)')), # class name
28 (1, re.compile(r'export\s+(class\s+\w+)')), # export class name
29 (1, re.compile(r'export\s+enum\s+(\w+)')), # function name
30 (1, re.compile(r'export\s+const\s+enum\s+(\w+)')), # function name
31 (1, re.compile(r'export\s+function\s+(\w+)')), # function name
32 (1, re.compile(r'export\s+interface\s+(\w+)')), # interface name
33 (1, re.compile(r'function\s+(\w+)')), # function name
34 (1, re.compile(r'(constructor).*{')), # constructor ... {
35 # kind function name
36 (2, re.compile(r'%s\s*function\s+(\w+)' % kinds)),
37 # kind kind function name
38 (3, re.compile(r'%s\s+%s\s+function\s+(\w+)' % (kinds, kinds))),
39 # Bare functions last...
40 # kind kind name (...) {
41 (3, re.compile(r'%s\s+%s\s+(\w+)\s*\(.*\).*{' % (kinds, kinds))),
42 # name (...) {
43 (2, re.compile(r'%s\s+(\w+)\s*\(.*\).*{' % kinds)),
44 # #1619: Don't allow completely bare functions.
45 # (1, re.compile(r'(\w+)\s*\(.*\).*{')),
46 # name (...) {
47 )
48 #@-<< define function patterns >>
50 def __init__(self, importCommands, **kwargs):
51 """The ctor for the TS_ImportController class."""
52 # Init the base class.
53 super().__init__(
54 importCommands,
55 language='typescript', # Case is important.
56 state_class=TS_ScanState,
57 )
59 #@+others
60 #@+node:ekr.20190830160459.1: *3* ts_i.add_class_names
61 def add_class_names(self, p):
62 """Add class names to headlines for all descendant nodes."""
63 return
64 #@+node:ekr.20161118093751.5: *3* ts_i.clean_headline
65 def clean_headline(self, s, p=None):
66 """Return a cleaned up headline s."""
67 s = s.strip()
68 # Don't clean a headline twice.
69 if s.endswith('>>') and s.startswith('<<'):
70 return s
71 # Try to match patterns.
72 for group_n, pattern in self.function_patterns:
73 m = pattern.match(s)
74 if m:
75 # g.trace('group %s: %s' % (group_n, m.group(group_n)))
76 return m.group(group_n)
77 # Final cleanups, if nothing matches.
78 for ch in '{(=':
79 if s.endswith(ch):
80 s = s[:-1].strip()
81 s = s.replace(' ', ' ')
82 s = s.replace(' (', '(')
83 return g.truncate(s, 100)
84 #@+node:ekr.20200816192919.1: *3* ts_i.promote_last_lines
85 comment_pat = re.compile(r'(/\*.*?\*/)', re.DOTALL | re.MULTILINE)
87 def promote_last_lines(self, parent):
88 """
89 This method is slightly misnamed. It moves trailing comments to the
90 next node.
91 """
92 # Move trailing comments into following nodes.
93 for p in parent.self_and_subtree():
94 next = p.threadNext()
95 #
96 # Ensure next is in the proper tree.
97 ok = next and self.root.isAncestorOf(next) and self.has_lines(next)
98 if not ok:
99 continue
100 lines = self.get_lines(p)
101 if not lines:
102 continue
103 all_s = ''.join(lines)
104 #
105 # An ugly special case to avoid improperly-created children.
106 if p.hasChildren() and next != p.next():
107 next = p.next()
108 ok = next and self.root.isAncestorOf(next) and self.has_lines(next)
109 if not ok:
110 continue
111 all_matches = list(self.comment_pat.finditer(all_s))
112 m = all_matches and all_matches[-1]
113 if not m:
114 continue
115 comment_s = m.group(0)
116 i = m.start()
117 head_s = all_s[:i]
118 tail_s = all_s[i + len(comment_s) :]
119 if tail_s.strip():
120 continue # Not a trailing comment.
121 head_lines = g.splitLines(head_s)
122 comment_lines = g.splitLines(comment_s + tail_s)
123 self.set_lines(p, head_lines)
124 self.prepend_lines(next, comment_lines)
125 #@+node:ekr.20161118093751.2: *3* ts_i.skip_possible_regex
126 def skip_possible_regex(self, s, i):
127 """look ahead for a regex /"""
128 assert s[i] in '=(', repr(s[i])
129 i += 1
130 while i < len(s) and s[i] in ' \t':
131 i += 1
132 if i < len(s) and s[i] == '/':
133 i += 1
134 while i < len(s):
135 progress = i
136 ch = s[i]
137 if ch == '\\':
138 i += 2
139 elif ch == '/':
140 i += 1
141 break
142 else:
143 i += 1
144 assert progress < i
146 return i - 1
147 #@+node:ekr.20180523170649.1: *3* ts_i.starts_block
148 def starts_block(self, i, lines, new_state, prev_state):
149 """True if the new state starts a block."""
150 if new_state.level() <= prev_state.level():
151 return False
152 line = lines[i].strip()
153 for word in ('do', 'else', 'for', 'if', 'switch', 'try', 'while'):
154 if line.startswith(word):
155 return False
156 # #1617: Chained calls look like functions, but aren't.
157 for pattern in self.non_function_patterns:
158 if pattern.match(line) is not None:
159 return False
160 for group_n, pattern in self.function_patterns:
161 if pattern.match(line) is not None:
162 return True
163 return False
164 #@-others
165#@+node:ekr.20161118071747.14: ** class TS_ScanState
166class TS_ScanState:
167 """A class representing the state of the typescript line-oriented scan."""
169 def __init__(self, d=None):
170 """TS_ScanState ctor."""
171 if d:
172 prev = d.get('prev')
173 self.context = prev.context
174 self.curlies = prev.curlies
175 else:
176 self.context = ''
177 self.curlies = 0
179 #@+others
180 #@+node:ekr.20161118071747.15: *3* ts_state.__repr__
181 def __repr__(self):
182 """ts_state.__repr__"""
183 return '<TS_State %r curlies: %s>' % (self.context, self.curlies)
185 __str__ = __repr__
186 #@+node:ekr.20161119115736.1: *3* ts_state.level
187 def level(self):
188 """TS_ScanState.level."""
189 return self.curlies
190 #@+node:ekr.20161118082821.1: *3* ts_state.is_ws_line
191 ws_pattern = re.compile(r'^\s*$|^\s*#')
193 def is_ws_line(self, s):
194 """Return True if s is nothing but whitespace and single-line comments."""
195 return bool(self.ws_pattern.match(s))
196 #@+node:ekr.20161118072957.1: *3* ts_state.update
197 def update(self, data):
198 """
199 Update the state using the 6-tuple returned by i.scan_line.
200 Return i = data[1]
201 """
202 context, i, delta_c, delta_p, delta_s, bs_nl = data
203 self.context = context
204 self.curlies += delta_c
205 return i
207 #@-others
208#@-others
209importer_dict = {
210 'func': TS_Importer.do_import(),
211 'extensions': ['.ts',],
212}
213#@@language python
214#@@tabwidth -4
215#@-leo