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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

112 statements  

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): 

12 

13 #@+<< define non-function patterns >> 

14 #@+node:ekr.20200817090227.1: *3* << define non-function patterns >> 

15 non_function_patterns = ( 

16 

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 >> 

59 

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 ) 

68 

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) 

96 

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 

155 

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.""" 

178 

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 

188 

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) 

194 

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*#') 

202 

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 

216 

217 #@-others 

218#@-others 

219importer_dict = { 

220 'func': TS_Importer.do_import(), 

221 'extensions': ['.ts',], 

222} 

223#@@language python 

224#@@tabwidth -4 

225#@-leo