src/eric7/Utilities/ClassBrowsers/rbclbr.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
17 import Utilities 17 import Utilities
18 import Utilities.ClassBrowsers as ClassBrowsers 18 import Utilities.ClassBrowsers as ClassBrowsers
19 from . import ClbrBaseClasses 19 from . import ClbrBaseClasses
20 20
21 SUPPORTED_TYPES = [ClassBrowsers.RB_SOURCE] 21 SUPPORTED_TYPES = [ClassBrowsers.RB_SOURCE]
22 22
23 _getnext = re.compile( 23 _getnext = re.compile(
24 r""" 24 r"""
25 (?P<String> 25 (?P<String>
26 =begin .*? =end 26 =begin .*? =end
27 27
154 end [ \t]* $ 154 end [ \t]* $
155 | 155 |
156 end \b [^_] 156 end \b [^_]
157 ) 157 )
158 )""", 158 )""",
159 re.VERBOSE | re.DOTALL | re.MULTILINE).search 159 re.VERBOSE | re.DOTALL | re.MULTILINE,
160 ).search
160 161
161 _commentsub = re.compile(r"""#[^\n]*\n|#[^\n]*$""").sub 162 _commentsub = re.compile(r"""#[^\n]*\n|#[^\n]*$""").sub
162 163
163 _modules = {} # cache of modules we've seen 164 _modules = {} # cache of modules we've seen
164 165
165 166
166 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): 167 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase):
167 """ 168 """
168 Mixin class implementing the notion of visibility. 169 Mixin class implementing the notion of visibility.
169 """ 170 """
171
170 def __init__(self): 172 def __init__(self):
171 """ 173 """
172 Constructor 174 Constructor
173 """ 175 """
174 self.setPublic() 176 self.setPublic()
176 178
177 class Class(ClbrBaseClasses.Class, VisibilityMixin): 179 class Class(ClbrBaseClasses.Class, VisibilityMixin):
178 """ 180 """
179 Class to represent a Ruby class. 181 Class to represent a Ruby class.
180 """ 182 """
183
181 def __init__(self, module, name, superClasses, file, lineno): 184 def __init__(self, module, name, superClasses, file, lineno):
182 """ 185 """
183 Constructor 186 Constructor
184 187
185 @param module name of the module containing this class 188 @param module name of the module containing this class
186 @param name name of this class 189 @param name name of this class
187 @param superClasses list of class names this class is inherited from 190 @param superClasses list of class names this class is inherited from
188 @param file filename containing this class 191 @param file filename containing this class
189 @param lineno linenumber of the class definition 192 @param lineno linenumber of the class definition
190 """ 193 """
191 ClbrBaseClasses.Class.__init__(self, module, name, superClasses, file, 194 ClbrBaseClasses.Class.__init__(self, module, name, superClasses, file, lineno)
192 lineno)
193 VisibilityMixin.__init__(self) 195 VisibilityMixin.__init__(self)
194 196
195 197
196 class Module(ClbrBaseClasses.Module, VisibilityMixin): 198 class Module(ClbrBaseClasses.Module, VisibilityMixin):
197 """ 199 """
198 Class to represent a Ruby module. 200 Class to represent a Ruby module.
199 """ 201 """
202
200 def __init__(self, module, name, file, lineno): 203 def __init__(self, module, name, file, lineno):
201 """ 204 """
202 Constructor 205 Constructor
203 206
204 @param module name of the module containing this class 207 @param module name of the module containing this class
205 @param name name of this class 208 @param name name of this class
206 @param file filename containing this class 209 @param file filename containing this class
207 @param lineno linenumber of the class definition 210 @param lineno linenumber of the class definition
208 """ 211 """
212 215
213 class Function(ClbrBaseClasses.Function, VisibilityMixin): 216 class Function(ClbrBaseClasses.Function, VisibilityMixin):
214 """ 217 """
215 Class to represent a Ruby function. 218 Class to represent a Ruby function.
216 """ 219 """
217 def __init__(self, module, name, file, lineno, signature='', 220
218 separator=','): 221 def __init__(self, module, name, file, lineno, signature="", separator=","):
219 """ 222 """
220 Constructor 223 Constructor
221 224
222 @param module name of the module containing this function 225 @param module name of the module containing this function
223 @param name name of this function 226 @param name name of this function
224 @param file filename containing this class 227 @param file filename containing this class
225 @param lineno linenumber of the class definition 228 @param lineno linenumber of the class definition
226 @param signature parameterlist of the method 229 @param signature parameterlist of the method
227 @param separator string separating the parameters 230 @param separator string separating the parameters
228 """ 231 """
229 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, 232 ClbrBaseClasses.Function.__init__(
230 signature, separator) 233 self, module, name, file, lineno, signature, separator
234 )
231 VisibilityMixin.__init__(self) 235 VisibilityMixin.__init__(self)
232 236
233 237
234 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): 238 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin):
235 """ 239 """
236 Class to represent a class or module attribute. 240 Class to represent a class or module attribute.
237 """ 241 """
242
238 def __init__(self, module, name, file, lineno): 243 def __init__(self, module, name, file, lineno):
239 """ 244 """
240 Constructor 245 Constructor
241 246
242 @param module name of the module containing this class 247 @param module name of the module containing this class
243 @param name name of this class 248 @param name name of this class
244 @param file filename containing this attribute 249 @param file filename containing this attribute
245 @param lineno linenumber of the class definition 250 @param lineno linenumber of the class definition
246 """ 251 """
256 @param module name of the Ruby file (string) 261 @param module name of the Ruby file (string)
257 @param path path the file should be searched in (list of strings) 262 @param path path the file should be searched in (list of strings)
258 @return the resulting dictionary 263 @return the resulting dictionary
259 """ 264 """
260 global _modules 265 global _modules
261 266
262 if module in _modules: 267 if module in _modules:
263 # we've seen this file before... 268 # we've seen this file before...
264 return _modules[module] 269 return _modules[module]
265 270
266 # search the path for the file 271 # search the path for the file
278 src = Utilities.readEncodedFile(file)[0] 283 src = Utilities.readEncodedFile(file)[0]
279 except (UnicodeError, OSError): 284 except (UnicodeError, OSError):
280 # can't do anything with this module 285 # can't do anything with this module
281 _modules[module] = {} 286 _modules[module] = {}
282 return {} 287 return {}
283 288
284 _modules[module] = scan(src, file, module) 289 _modules[module] = scan(src, file, module)
285 return _modules[module] 290 return _modules[module]
286 291
287 292
288 def scan(src, file, module): 293 def scan(src, file, module):
289 """ 294 """
290 Public method to scan the given source text. 295 Public method to scan the given source text.
291 296
292 @param src source text to be scanned 297 @param src source text to be scanned
293 @type str 298 @type str
294 @param file file name associated with the source text 299 @param file file name associated with the source text
295 @type str 300 @type str
296 @param module module name associated with the source text 301 @param module module name associated with the source text
303 308
304 dictionary = {} 309 dictionary = {}
305 dict_counts = {} 310 dict_counts = {}
306 311
307 classstack = [] # stack of (class, indent) pairs 312 classstack = [] # stack of (class, indent) pairs
308 acstack = [] # stack of (access control, indent) pairs 313 acstack = [] # stack of (access control, indent) pairs
309 indent = 0 314 indent = 0
310 315
311 lineno, last_lineno_pos = 1, 0 316 lineno, last_lineno_pos = 1, 0
312 cur_obj = None 317 cur_obj = None
313 lastGlobalEntry = None 318 lastGlobalEntry = None
321 if m.start("Method") >= 0: 326 if m.start("Method") >= 0:
322 # found a method definition or function 327 # found a method definition or function
323 thisindent = indent 328 thisindent = indent
324 indent += 1 329 indent += 1
325 meth_name = ( 330 meth_name = (
326 m.group("MethodName") or 331 m.group("MethodName")
327 m.group("MethodName2") or 332 or m.group("MethodName2")
328 m.group("MethodName3") 333 or m.group("MethodName3")
329 ) 334 )
330 meth_sig = m.group("MethodSignature") 335 meth_sig = m.group("MethodSignature")
331 meth_sig = meth_sig and meth_sig.replace('\\\n', '') or '' 336 meth_sig = meth_sig and meth_sig.replace("\\\n", "") or ""
332 meth_sig = _commentsub('', meth_sig) 337 meth_sig = _commentsub("", meth_sig)
333 lineno += src.count('\n', last_lineno_pos, start) 338 lineno += src.count("\n", last_lineno_pos, start)
334 last_lineno_pos = start 339 last_lineno_pos = start
335 if meth_name.startswith('self.'): 340 if meth_name.startswith("self."):
336 meth_name = meth_name[5:] 341 meth_name = meth_name[5:]
337 elif meth_name.startswith('self::'): 342 elif meth_name.startswith("self::"):
338 meth_name = meth_name[6:] 343 meth_name = meth_name[6:]
339 # close all classes/modules indented at least as much 344 # close all classes/modules indented at least as much
340 while classstack and classstack[-1][1] >= thisindent: 345 while classstack and classstack[-1][1] >= thisindent:
341 if classstack[-1][0] is not None: 346 if classstack[-1][0] is not None:
342 # record the end line 347 # record the end line
347 if classstack: 352 if classstack:
348 # it's a class/module method 353 # it's a class/module method
349 cur_class = classstack[-1][0] 354 cur_class = classstack[-1][0]
350 if isinstance(cur_class, (Class, Module)): 355 if isinstance(cur_class, (Class, Module)):
351 # it's a method 356 # it's a method
352 f = Function(None, meth_name, 357 f = Function(None, meth_name, file, lineno, meth_sig)
353 file, lineno, meth_sig)
354 cur_class._addmethod(meth_name, f) 358 cur_class._addmethod(meth_name, f)
355 else: 359 else:
356 f = cur_class 360 f = cur_class
357 # set access control 361 # set access control
358 if acstack: 362 if acstack:
364 elif accesscontrol == "public": 368 elif accesscontrol == "public":
365 f.setPublic() 369 f.setPublic()
366 # else it's a nested def 370 # else it's a nested def
367 else: 371 else:
368 # it's a function 372 # it's a function
369 f = Function(module, meth_name, 373 f = Function(module, meth_name, file, lineno, meth_sig)
370 file, lineno, meth_sig)
371 if meth_name in dict_counts: 374 if meth_name in dict_counts:
372 dict_counts[meth_name] += 1 375 dict_counts[meth_name] += 1
373 meth_name = "{0}_{1:d}".format( 376 meth_name = "{0}_{1:d}".format(meth_name, dict_counts[meth_name])
374 meth_name, dict_counts[meth_name])
375 else: 377 else:
376 dict_counts[meth_name] = 0 378 dict_counts[meth_name] = 0
377 dictionary[meth_name] = f 379 dictionary[meth_name] = f
378 if not classstack: 380 if not classstack:
379 if lastGlobalEntry: 381 if lastGlobalEntry:
383 cur_obj.setEndLine(lineno - 1) 385 cur_obj.setEndLine(lineno - 1)
384 cur_obj = f 386 cur_obj = f
385 classstack.append((f, thisindent)) # Marker for nested fns 387 classstack.append((f, thisindent)) # Marker for nested fns
386 388
387 elif ( 389 elif (
388 m.start("String") >= 0 or 390 m.start("String") >= 0
389 m.start("Comment") >= 0 or 391 or m.start("Comment") >= 0
390 m.start("ClassIgnored") >= 0 or 392 or m.start("ClassIgnored") >= 0
391 m.start("BeginEnd") >= 0 393 or m.start("BeginEnd") >= 0
392 ): 394 ):
393 pass 395 pass
394 396
395 elif m.start("Class") >= 0: 397 elif m.start("Class") >= 0:
396 # we found a class definition 398 # we found a class definition
397 thisindent = indent 399 thisindent = indent
398 indent += 1 400 indent += 1
399 lineno += src.count('\n', last_lineno_pos, start) 401 lineno += src.count("\n", last_lineno_pos, start)
400 last_lineno_pos = start 402 last_lineno_pos = start
401 # close all classes/modules indented at least as much 403 # close all classes/modules indented at least as much
402 while classstack and classstack[-1][1] >= thisindent: 404 while classstack and classstack[-1][1] >= thisindent:
403 if classstack[-1][0] is not None: 405 if classstack[-1][0] is not None:
404 # record the end line 406 # record the end line
407 class_name = m.group("ClassName") or m.group("ClassName2") 409 class_name = m.group("ClassName") or m.group("ClassName2")
408 inherit = m.group("ClassSupers") 410 inherit = m.group("ClassSupers")
409 if inherit: 411 if inherit:
410 # the class inherits from other classes 412 # the class inherits from other classes
411 inherit = inherit[1:].strip() 413 inherit = inherit[1:].strip()
412 inherit = [_commentsub('', inherit)] 414 inherit = [_commentsub("", inherit)]
413 # remember this class 415 # remember this class
414 cur_class = Class(module, class_name, inherit, 416 cur_class = Class(module, class_name, inherit, file, lineno)
415 file, lineno)
416 if not classstack: 417 if not classstack:
417 if class_name in dictionary: 418 if class_name in dictionary:
418 cur_class = dictionary[class_name] 419 cur_class = dictionary[class_name]
419 else: 420 else:
420 dictionary[class_name] = cur_class 421 dictionary[class_name] = cur_class
439 440
440 elif m.start("Module") >= 0: 441 elif m.start("Module") >= 0:
441 # we found a module definition 442 # we found a module definition
442 thisindent = indent 443 thisindent = indent
443 indent += 1 444 indent += 1
444 lineno += src.count('\n', last_lineno_pos, start) 445 lineno += src.count("\n", last_lineno_pos, start)
445 last_lineno_pos = start 446 last_lineno_pos = start
446 # close all classes/modules indented at least as much 447 # close all classes/modules indented at least as much
447 while classstack and classstack[-1][1] >= thisindent: 448 while classstack and classstack[-1][1] >= thisindent:
448 if classstack[-1][0] is not None: 449 if classstack[-1][0] is not None:
449 # record the end line 450 # record the end line
481 if aclist is None: 482 if aclist is None:
482 index = -1 483 index = -1
483 while index >= -len(acstack): 484 while index >= -len(acstack):
484 if acstack[index][1] < indent: 485 if acstack[index][1] < indent:
485 actype = ( 486 actype = (
486 m.group("AccessControlType") or 487 m.group("AccessControlType")
487 m.group("AccessControlType2").split('_')[0] 488 or m.group("AccessControlType2").split("_")[0]
488 ) 489 )
489 acstack[index][0] = actype.lower() 490 acstack[index][0] = actype.lower()
490 break 491 break
491 else: 492 else:
492 index -= 1 493 index -= 1
493 else: 494 else:
494 index = -1 495 index = -1
495 while index >= -len(classstack): 496 while index >= -len(classstack):
496 if ( 497 if (
497 classstack[index][0] is not None and 498 classstack[index][0] is not None
498 not isinstance(classstack[index][0], Function) and 499 and not isinstance(classstack[index][0], Function)
499 classstack[index][1] < indent 500 and classstack[index][1] < indent
500 ): 501 ):
501 parent = classstack[index][0] 502 parent = classstack[index][0]
502 actype = ( 503 actype = (
503 m.group("AccessControlType") or 504 m.group("AccessControlType")
504 m.group("AccessControlType2").split('_')[0] 505 or m.group("AccessControlType2").split("_")[0]
505 ) 506 )
506 actype = actype.lower() 507 actype = actype.lower()
507 for name in aclist.split(","): 508 for name in aclist.split(","):
508 name = name.strip()[1:] # get rid of leading ':' 509 name = name.strip()[1:] # get rid of leading ':'
509 acmeth = parent._getmethod(name) 510 acmeth = parent._getmethod(name)
510 if acmeth is None: 511 if acmeth is None:
511 continue 512 continue
512 if actype == "private": 513 if actype == "private":
513 acmeth.setPrivate() 514 acmeth.setPrivate()
518 break 519 break
519 else: 520 else:
520 index -= 1 521 index -= 1
521 522
522 elif m.start("Attribute") >= 0: 523 elif m.start("Attribute") >= 0:
523 lineno += src.count('\n', last_lineno_pos, start) 524 lineno += src.count("\n", last_lineno_pos, start)
524 last_lineno_pos = start 525 last_lineno_pos = start
525 index = -1 526 index = -1
526 while index >= -len(classstack): 527 while index >= -len(classstack):
527 if ( 528 if (
528 classstack[index][0] is not None and 529 classstack[index][0] is not None
529 not isinstance(classstack[index][0], Function) and 530 and not isinstance(classstack[index][0], Function)
530 classstack[index][1] < indent 531 and classstack[index][1] < indent
531 ): 532 ):
532 attr = Attribute( 533 attr = Attribute(module, m.group("AttributeName"), file, lineno)
533 module, m.group("AttributeName"), file, lineno)
534 classstack[index][0]._addattribute(attr) 534 classstack[index][0]._addattribute(attr)
535 break 535 break
536 else: 536 else:
537 index -= 1 537 index -= 1
538 if lastGlobalEntry: 538 if lastGlobalEntry:
539 lastGlobalEntry.setEndLine(lineno - 1) 539 lastGlobalEntry.setEndLine(lineno - 1)
540 lastGlobalEntry = None 540 lastGlobalEntry = None
541 541
542 elif m.start("Attr") >= 0: 542 elif m.start("Attr") >= 0:
543 lineno += src.count('\n', last_lineno_pos, start) 543 lineno += src.count("\n", last_lineno_pos, start)
544 last_lineno_pos = start 544 last_lineno_pos = start
545 index = -1 545 index = -1
546 while index >= -len(classstack): 546 while index >= -len(classstack):
547 if ( 547 if (
548 classstack[index][0] is not None and 548 classstack[index][0] is not None
549 not isinstance(classstack[index][0], Function) and 549 and not isinstance(classstack[index][0], Function)
550 classstack[index][1] < indent 550 and classstack[index][1] < indent
551 ): 551 ):
552 parent = classstack[index][0] 552 parent = classstack[index][0]
553 if m.group("AttrType") is None: 553 if m.group("AttrType") is None:
554 nv = m.group("AttrList").split(",") 554 nv = m.group("AttrList").split(",")
555 if not nv: 555 if not nv:
556 break 556 break
557 name = nv[0].strip()[1:] # get rid of leading ':' 557 name = nv[0].strip()[1:] # get rid of leading ':'
558 attr = ( 558 attr = (
559 parent._getattribute("@" + name) or 559 parent._getattribute("@" + name)
560 parent._getattribute("@@" + name) or 560 or parent._getattribute("@@" + name)
561 Attribute(module, "@" + name, file, lineno) 561 or Attribute(module, "@" + name, file, lineno)
562 ) 562 )
563 if len(nv) == 1 or nv[1].strip() == "false": 563 if len(nv) == 1 or nv[1].strip() == "false":
564 attr.setProtected() 564 attr.setProtected()
565 elif nv[1].strip() == "true": 565 elif nv[1].strip() == "true":
566 attr.setPublic() 566 attr.setPublic()
567 parent._addattribute(attr) 567 parent._addattribute(attr)
568 else: 568 else:
569 access = m.group("AttrType") 569 access = m.group("AttrType")
570 for name in m.group("AttrList").split(","): 570 for name in m.group("AttrList").split(","):
571 name = name.strip()[1:] # get rid of leading ':' 571 name = name.strip()[1:] # get rid of leading ':'
572 attr = ( 572 attr = (
573 parent._getattribute("@" + name) or 573 parent._getattribute("@" + name)
574 parent._getattribute("@@" + name) or 574 or parent._getattribute("@@" + name)
575 Attribute(module, "@" + name, file, lineno) 575 or Attribute(module, "@" + name, file, lineno)
576 ) 576 )
577 if access == "_accessor": 577 if access == "_accessor":
578 attr.setPublic() 578 attr.setPublic()
579 elif access in ("_reader", "_writer"): 579 elif access in ("_reader", "_writer"):
580 if attr.isPrivate(): 580 if attr.isPrivate():
598 if classstack: 598 if classstack:
599 # it's a class/module method 599 # it's a class/module method
600 indent = classstack[-1][1] 600 indent = classstack[-1][1]
601 else: 601 else:
602 indent = 0 602 indent = 0
603 603
604 elif m.start("CodingLine") >= 0: 604 elif m.start("CodingLine") >= 0:
605 # a coding statement 605 # a coding statement
606 coding = m.group("Coding") 606 coding = m.group("Coding")
607 lineno += src.count('\n', last_lineno_pos, start) 607 lineno += src.count("\n", last_lineno_pos, start)
608 last_lineno_pos = start 608 last_lineno_pos = start
609 if "@@Coding@@" not in dictionary: 609 if "@@Coding@@" not in dictionary:
610 dictionary["@@Coding@@"] = ClbrBaseClasses.Coding( 610 dictionary["@@Coding@@"] = ClbrBaseClasses.Coding(
611 module, file, lineno, coding) 611 module, file, lineno, coding
612 )
612 613
613 return dictionary 614 return dictionary

eric ide

mercurial