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

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+)')), # 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 >> 

49 

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 ) 

58 

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) 

86 

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 

145 

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

168 

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 

178 

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) 

184 

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

192 

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 

206 

207 #@-others 

208#@-others 

209importer_dict = { 

210 'func': TS_Importer.do_import(), 

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

212} 

213#@@language python 

214#@@tabwidth -4 

215#@-leo