Coverage for C:\leo.repo\leo-editor\leo\plugins\importers\typescript.py : 65%

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