src/eric7/Utilities/ClassBrowsers/jsclbr.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
15 import Utilities 15 import Utilities
16 import Utilities.ClassBrowsers as ClassBrowsers 16 import Utilities.ClassBrowsers as ClassBrowsers
17 from . import ClbrBaseClasses 17 from . import ClbrBaseClasses
18 18
19 SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE] 19 SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE]
20 20
21 _modules = {} # cache of modules we've seen 21 _modules = {} # cache of modules we've seen
22 22
23 23
24 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): 24 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase):
25 """ 25 """
26 Mixin class implementing the notion of visibility. 26 Mixin class implementing the notion of visibility.
27 """ 27 """
28
28 def __init__(self): 29 def __init__(self):
29 """ 30 """
30 Constructor 31 Constructor
31 """ 32 """
32 if self.name.startswith('__'): 33 if self.name.startswith("__"):
33 self.setPrivate() 34 self.setPrivate()
34 elif self.name.startswith('_'): 35 elif self.name.startswith("_"):
35 self.setProtected() 36 self.setProtected()
36 else: 37 else:
37 self.setPublic() 38 self.setPublic()
38 39
39 40
40 class Function(ClbrBaseClasses.Function, VisibilityMixin): 41 class Function(ClbrBaseClasses.Function, VisibilityMixin):
41 """ 42 """
42 Class to represent a Python function. 43 Class to represent a Python function.
43 """ 44 """
44 def __init__(self, module, name, file, lineno, signature='', 45
45 separator=','): 46 def __init__(self, module, name, file, lineno, signature="", separator=","):
46 """ 47 """
47 Constructor 48 Constructor
48 49
49 @param module name of the module containing this function 50 @param module name of the module containing this function
50 @param name name of this function 51 @param name name of this function
51 @param file filename containing this class 52 @param file filename containing this class
52 @param lineno linenumber of the class definition 53 @param lineno linenumber of the class definition
53 @param signature parameterlist of the method 54 @param signature parameterlist of the method
54 @param separator string separating the parameters 55 @param separator string separating the parameters
55 """ 56 """
56 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, 57 ClbrBaseClasses.Function.__init__(
57 signature, separator) 58 self, module, name, file, lineno, signature, separator
59 )
58 VisibilityMixin.__init__(self) 60 VisibilityMixin.__init__(self)
59 61
60 62
61 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): 63 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin):
62 """ 64 """
63 Class to represent a class attribute. 65 Class to represent a class attribute.
64 """ 66 """
67
65 def __init__(self, module, name, file, lineno): 68 def __init__(self, module, name, file, lineno):
66 """ 69 """
67 Constructor 70 Constructor
68 71
69 @param module name of the module containing this class 72 @param module name of the module containing this class
70 @param name name of this class 73 @param name name of this class
71 @param file filename containing this attribute 74 @param file filename containing this attribute
72 @param lineno linenumber of the class definition 75 @param lineno linenumber of the class definition
73 """ 76 """
77 80
78 class Visitor: 81 class Visitor:
79 """ 82 """
80 Class implementing a visitor going through the parsed tree. 83 Class implementing a visitor going through the parsed tree.
81 """ 84 """
85
82 def __init__(self, src, module, filename): 86 def __init__(self, src, module, filename):
83 """ 87 """
84 Constructor 88 Constructor
85 89
86 @param src source to be parsed (string) 90 @param src source to be parsed (string)
87 @param module name of the module (string) 91 @param module name of the module (string)
88 @param filename file name (string) 92 @param filename file name (string)
89 """ 93 """
90 self.__dict = {} 94 self.__dict = {}
91 self.__dict_counts = {} 95 self.__dict_counts = {}
92 self.__root = None 96 self.__root = None
93 self.__stack = [] 97 self.__stack = []
94 98
95 self.__module = module 99 self.__module = module
96 self.__file = filename 100 self.__file = filename
97 self.__source = src 101 self.__source = src
98 102
99 # normalize line endings 103 # normalize line endings
100 self.__source = self.__source.replace("\r\n", "\n").replace("\r", "\n") 104 self.__source = self.__source.replace("\r\n", "\n").replace("\r", "\n")
101 105
102 # ensure source ends with an eol 106 # ensure source ends with an eol
103 if bool(self.__source) and self.__source[-1] != '\n': 107 if bool(self.__source) and self.__source[-1] != "\n":
104 self.__source = self.__source + '\n' 108 self.__source = self.__source + "\n"
105 109
106 def parse(self): 110 def parse(self):
107 """ 111 """
108 Public method to parse the source. 112 Public method to parse the source.
109 113
110 @return dictionary containing the parsed information 114 @return dictionary containing the parsed information
111 """ 115 """
112 try: 116 try:
113 self.__root = jsParser.parse(self.__source, self.__file) 117 self.__root = jsParser.parse(self.__source, self.__file)
114 self.__visit(self.__root) 118 self.__visit(self.__root)
116 # ignore syntax errors of the parser 120 # ignore syntax errors of the parser
117 pass 121 pass
118 except jsTokenizer.ParseError: 122 except jsTokenizer.ParseError:
119 # ignore syntax errors of the tokenizer 123 # ignore syntax errors of the tokenizer
120 pass 124 pass
121 125
122 return self.__dict 126 return self.__dict
123 127
124 def __visit(self, root): 128 def __visit(self, root):
125 """ 129 """
126 Private method implementing the visit logic delegating to interesting 130 Private method implementing the visit logic delegating to interesting
127 methods. 131 methods.
128 132
129 @param root root node to visit 133 @param root root node to visit
130 """ 134 """
135
131 def call(n): 136 def call(n):
132 getattr(self, "visit_{0}".format(n.type), 137 getattr(self, "visit_{0}".format(n.type), self.visit_noop)(n)
133 self.visit_noop)(n) 138
134
135 call(root) 139 call(root)
136 for node in root: 140 for node in root:
137 self.__visit(node) 141 self.__visit(node)
138 142
139 def visit_noop(self, node): 143 def visit_noop(self, node):
140 """ 144 """
141 Public method to ignore the given node. 145 Public method to ignore the given node.
142 146
143 @param node reference to the node (jasy.script.parse.Node.Node) 147 @param node reference to the node (jasy.script.parse.Node.Node)
144 """ 148 """
145 pass 149 pass
146 150
147 def visit_function(self, node): 151 def visit_function(self, node):
148 """ 152 """
149 Public method to treat a function node. 153 Public method to treat a function node.
150 154
151 @param node reference to the node (jasy.script.parse.Node.Node) 155 @param node reference to the node (jasy.script.parse.Node.Node)
152 """ 156 """
153 if ( 157 if (
154 node.type == "function" and 158 node.type == "function"
155 getattr(node, "name", None) and 159 and getattr(node, "name", None)
156 node.functionForm == "declared_form" 160 and node.functionForm == "declared_form"
157 ): 161 ):
158 if self.__stack and self.__stack[-1].endlineno < node.line: 162 if self.__stack and self.__stack[-1].endlineno < node.line:
159 del self.__stack[-1] 163 del self.__stack[-1]
160 endline = node.line + self.__source.count( 164 endline = node.line + self.__source.count("\n", node.start, node.end)
161 '\n', node.start, node.end)
162 if getattr(node, "params", None): 165 if getattr(node, "params", None):
163 func_sig = ", ".join([p.value for p in node.params]) 166 func_sig = ", ".join([p.value for p in node.params])
164 else: 167 else:
165 func_sig = "" 168 func_sig = ""
166 if self.__stack: 169 if self.__stack:
167 # it's a nested function 170 # it's a nested function
168 cur_func = self.__stack[-1] 171 cur_func = self.__stack[-1]
169 f = Function(None, node.name, 172 f = Function(None, node.name, self.__file, node.line, func_sig)
170 self.__file, node.line, func_sig)
171 f.setEndLine(endline) 173 f.setEndLine(endline)
172 cur_func._addmethod(node.name, f) 174 cur_func._addmethod(node.name, f)
173 else: 175 else:
174 f = Function(self.__module, node.name, 176 f = Function(self.__module, node.name, self.__file, node.line, func_sig)
175 self.__file, node.line, func_sig)
176 f.setEndLine(endline) 177 f.setEndLine(endline)
177 func_name = node.name 178 func_name = node.name
178 if func_name in self.__dict_counts: 179 if func_name in self.__dict_counts:
179 self.__dict_counts[func_name] += 1 180 self.__dict_counts[func_name] += 1
180 func_name = "{0}_{1:d}".format( 181 func_name = "{0}_{1:d}".format(
181 func_name, self.__dict_counts[func_name]) 182 func_name, self.__dict_counts[func_name]
183 )
182 else: 184 else:
183 self.__dict_counts[func_name] = 0 185 self.__dict_counts[func_name] = 0
184 self.__dict[func_name] = f 186 self.__dict[func_name] = f
185 self.__stack.append(f) 187 self.__stack.append(f)
186 188
187 def visit_property_init(self, node): 189 def visit_property_init(self, node):
188 """ 190 """
189 Public method to treat a property_init node. 191 Public method to treat a property_init node.
190 192
191 @param node reference to the node (jasy.script.parse.Node.Node) 193 @param node reference to the node (jasy.script.parse.Node.Node)
192 """ 194 """
193 if node.type == "property_init" and node[1].type == "function": 195 if node.type == "property_init" and node[1].type == "function":
194 if self.__stack and self.__stack[-1].endlineno < node[0].line: 196 if self.__stack and self.__stack[-1].endlineno < node[0].line:
195 del self.__stack[-1] 197 del self.__stack[-1]
196 endline = node[0].line + self.__source.count( 198 endline = node[0].line + self.__source.count("\n", node.start, node[1].end)
197 '\n', node.start, node[1].end)
198 if getattr(node[1], "params", None): 199 if getattr(node[1], "params", None):
199 func_sig = ", ".join([p.value for p in node[1].params]) 200 func_sig = ", ".join([p.value for p in node[1].params])
200 else: 201 else:
201 func_sig = "" 202 func_sig = ""
202 if self.__stack: 203 if self.__stack:
203 # it's a nested function 204 # it's a nested function
204 cur_func = self.__stack[-1] 205 cur_func = self.__stack[-1]
205 f = Function(None, node[0].value, 206 f = Function(None, node[0].value, self.__file, node[0].line, func_sig)
206 self.__file, node[0].line, func_sig)
207 f.setEndLine(endline) 207 f.setEndLine(endline)
208 cur_func._addmethod(node[0].value, f) 208 cur_func._addmethod(node[0].value, f)
209 else: 209 else:
210 f = Function(self.__module, node[0].value, 210 f = Function(
211 self.__file, node[0].line, func_sig) 211 self.__module, node[0].value, self.__file, node[0].line, func_sig
212 )
212 f.setEndLine(endline) 213 f.setEndLine(endline)
213 func_name = node[0].value 214 func_name = node[0].value
214 if func_name in self.__dict_counts: 215 if func_name in self.__dict_counts:
215 self.__dict_counts[func_name] += 1 216 self.__dict_counts[func_name] += 1
216 func_name = "{0}_{1:d}".format( 217 func_name = "{0}_{1:d}".format(
217 func_name, self.__dict_counts[func_name]) 218 func_name, self.__dict_counts[func_name]
219 )
218 else: 220 else:
219 self.__dict_counts[func_name] = 0 221 self.__dict_counts[func_name] = 0
220 self.__dict[func_name] = f 222 self.__dict[func_name] = f
221 self.__stack.append(f) 223 self.__stack.append(f)
222 224
223 def visit_var(self, node): 225 def visit_var(self, node):
224 """ 226 """
225 Public method to treat a variable node. 227 Public method to treat a variable node.
226 228
227 @param node reference to the node (jasy.script.parse.Node.Node) 229 @param node reference to the node (jasy.script.parse.Node.Node)
228 """ 230 """
229 if ( 231 if (
230 node.type == "var" and 232 node.type == "var"
231 node.parent.type == "script" and 233 and node.parent.type == "script"
232 node.getChildrenLength() 234 and node.getChildrenLength()
235 ):
236 if self.__stack and self.__stack[-1].endlineno < node[0].line:
237 del self.__stack[-1]
238 if self.__stack:
239 # function variables
240 for var in node:
241 attr = Attribute(self.__module, var.name, self.__file, var.line)
242 self.__stack[-1]._addattribute(attr)
243 else:
244 # global variable
245 if "@@Globals@@" not in self.__dict:
246 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase(
247 self.__module, "Globals", self.__file, 0
248 )
249 for var in node:
250 self.__dict["@@Globals@@"]._addglobal(
251 Attribute(self.__module, var.name, self.__file, var.line)
252 )
253
254 def visit_const(self, node):
255 """
256 Public method to treat a constant node.
257
258 @param node reference to the node (jasy.script.parse.Node.Node)
259 """
260 if (
261 node.type == "const"
262 and node.parent.type == "script"
263 and node.getChildrenLength()
233 ): 264 ):
234 if self.__stack and self.__stack[-1].endlineno < node[0].line: 265 if self.__stack and self.__stack[-1].endlineno < node[0].line:
235 del self.__stack[-1] 266 del self.__stack[-1]
236 if self.__stack: 267 if self.__stack:
237 # function variables 268 # function variables
238 for var in node: 269 for var in node:
239 attr = Attribute( 270 attr = Attribute(
240 self.__module, var.name, self.__file, var.line) 271 self.__module, "const " + var.name, self.__file, var.line
272 )
241 self.__stack[-1]._addattribute(attr) 273 self.__stack[-1]._addattribute(attr)
242 else: 274 else:
243 # global variable 275 # global variable
244 if "@@Globals@@" not in self.__dict: 276 if "@@Globals@@" not in self.__dict:
245 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( 277 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase(
246 self.__module, "Globals", self.__file, 0) 278 self.__module, "Globals", self.__file, 0
247 for var in node: 279 )
248 self.__dict["@@Globals@@"]._addglobal(Attribute(
249 self.__module, var.name, self.__file, var.line))
250
251 def visit_const(self, node):
252 """
253 Public method to treat a constant node.
254
255 @param node reference to the node (jasy.script.parse.Node.Node)
256 """
257 if (
258 node.type == "const" and
259 node.parent.type == "script" and
260 node.getChildrenLength()
261 ):
262 if self.__stack and self.__stack[-1].endlineno < node[0].line:
263 del self.__stack[-1]
264 if self.__stack:
265 # function variables
266 for var in node:
267 attr = Attribute(self.__module, "const " + var.name,
268 self.__file, var.line)
269 self.__stack[-1]._addattribute(attr)
270 else:
271 # global variable
272 if "@@Globals@@" not in self.__dict:
273 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase(
274 self.__module, "Globals", self.__file, 0)
275 for var in node: 280 for var in node:
276 self.__dict["@@Globals@@"]._addglobal( 281 self.__dict["@@Globals@@"]._addglobal(
277 Attribute(self.__module, "const " + var.name, 282 Attribute(
278 self.__file, var.line)) 283 self.__module, "const " + var.name, self.__file, var.line
284 )
285 )
279 286
280 287
281 def readmodule_ex(module, path=None): 288 def readmodule_ex(module, path=None):
282 """ 289 """
283 Read a JavaScript file and return a dictionary of functions and variables. 290 Read a JavaScript file and return a dictionary of functions and variables.
285 @param module name of the JavaScript file (string) 292 @param module name of the JavaScript file (string)
286 @param path path the file should be searched in (list of strings) 293 @param path path the file should be searched in (list of strings)
287 @return the resulting dictionary 294 @return the resulting dictionary
288 """ 295 """
289 global _modules 296 global _modules
290 297
291 if module in _modules: 298 if module in _modules:
292 # we've seen this file before... 299 # we've seen this file before...
293 return _modules[module] 300 return _modules[module]
294 301
295 # search the path for the file 302 # search the path for the file
307 src = Utilities.readEncodedFile(file)[0] 314 src = Utilities.readEncodedFile(file)[0]
308 except (UnicodeError, OSError): 315 except (UnicodeError, OSError):
309 # can't do anything with this module 316 # can't do anything with this module
310 _modules[module] = {} 317 _modules[module] = {}
311 return {} 318 return {}
312 319
313 _modules[module] = scan(src, file, module) 320 _modules[module] = scan(src, file, module)
314 return _modules[module] 321 return _modules[module]
315 322
316 323
317 def scan(src, file, module): 324 def scan(src, file, module):
318 """ 325 """
319 Public method to scan the given source text. 326 Public method to scan the given source text.
320 327
321 @param src source text to be scanned 328 @param src source text to be scanned
322 @type str 329 @type str
323 @param file file name associated with the source text 330 @param file file name associated with the source text
324 @type str 331 @type str
325 @param module module name associated with the source text 332 @param module module name associated with the source text
327 @return dictionary containing the extracted data 334 @return dictionary containing the extracted data
328 @rtype dict 335 @rtype dict
329 """ 336 """
330 # convert eol markers the Python style 337 # convert eol markers the Python style
331 src = src.replace("\r\n", "\n").replace("\r", "\n") 338 src = src.replace("\r\n", "\n").replace("\r", "\n")
332 339
333 dictionary = {} 340 dictionary = {}
334 341
335 visitor = Visitor(src, module, file) 342 visitor = Visitor(src, module, file)
336 dictionary = visitor.parse() 343 dictionary = visitor.parse()
337 _modules[module] = dictionary 344 _modules[module] = dictionary

eric ide

mercurial