--- a/src/eric7/Utilities/ClassBrowsers/jsclbr.py Fri Mar 31 13:39:51 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2013 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Parse a JavaScript file and retrieve variables and functions. - -It uses the JavaScript parser contained in the jasy web framework. -""" - -import jasy.script.parse.Parser as jsParser -import jasy.script.tokenize.Tokenizer as jsTokenizer - -from eric7 import Utilities -from eric7.Utilities import ClassBrowsers - -from . import ClbrBaseClasses - -SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE] - - -class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): - """ - Mixin class implementing the notion of visibility. - """ - - def __init__(self): - """ - Constructor - """ - if self.name.startswith("__"): - self.setPrivate() - elif self.name.startswith("_"): - self.setProtected() - else: - self.setPublic() - - -class Function(ClbrBaseClasses.Function, VisibilityMixin): - """ - Class to represent a Python function. - """ - - def __init__(self, module, name, file, lineno, signature="", separator=","): - """ - Constructor - - @param module name of the module containing this function - @param name name of this function - @param file filename containing this class - @param lineno linenumber of the class definition - @param signature parameterlist of the method - @param separator string separating the parameters - """ - ClbrBaseClasses.Function.__init__( - self, module, name, file, lineno, signature, separator - ) - VisibilityMixin.__init__(self) - - -class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): - """ - Class to represent a class attribute. - """ - - def __init__(self, module, name, file, lineno): - """ - Constructor - - @param module name of the module containing this class - @param name name of this class - @param file filename containing this attribute - @param lineno linenumber of the class definition - """ - ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno) - VisibilityMixin.__init__(self) - - -class Visitor: - """ - Class implementing a visitor going through the parsed tree. - """ - - def __init__(self, src, module, filename): - """ - Constructor - - @param src source to be parsed (string) - @param module name of the module (string) - @param filename file name (string) - """ - self.__dict = {} - self.__dict_counts = {} - self.__root = None - self.__stack = [] - - self.__module = module - self.__file = filename - self.__source = src - - # normalize line endings - self.__source = self.__source.replace("\r\n", "\n").replace("\r", "\n") - - # ensure source ends with an eol - if bool(self.__source) and self.__source[-1] != "\n": - self.__source = self.__source + "\n" - - def parse(self): - """ - Public method to parse the source. - - @return dictionary containing the parsed information - """ - try: - self.__root = jsParser.parse(self.__source, self.__file) - self.__visit(self.__root) - except jsParser.SyntaxError: - # ignore syntax errors of the parser - pass - except jsTokenizer.ParseError: - # ignore syntax errors of the tokenizer - pass - - return self.__dict - - def __visit(self, root): - """ - Private method implementing the visit logic delegating to interesting - methods. - - @param root root node to visit - """ - - def call(n): - getattr(self, "visit_{0}".format(n.type), self.visit_noop)(n) - - call(root) - for node in root: - self.__visit(node) - - def visit_noop(self, node): - """ - Public method to ignore the given node. - - @param node reference to the node (jasy.script.parse.Node.Node) - """ - pass - - def visit_function(self, node): - """ - Public method to treat a function node. - - @param node reference to the node (jasy.script.parse.Node.Node) - """ - if ( - node.type == "function" - and getattr(node, "name", None) - and node.functionForm == "declared_form" - ): - if self.__stack and self.__stack[-1].endlineno < node.line: - del self.__stack[-1] - endline = node.line + self.__source.count("\n", node.start, node.end) - if getattr(node, "params", None): - func_sig = ", ".join([p.value for p in node.params]) - else: - func_sig = "" - if self.__stack: - # it's a nested function - cur_func = self.__stack[-1] - f = Function(None, node.name, self.__file, node.line, func_sig) - f.setEndLine(endline) - cur_func._addmethod(node.name, f) - else: - f = Function(self.__module, node.name, self.__file, node.line, func_sig) - f.setEndLine(endline) - func_name = node.name - if func_name in self.__dict_counts: - self.__dict_counts[func_name] += 1 - func_name = "{0}_{1:d}".format( - func_name, self.__dict_counts[func_name] - ) - else: - self.__dict_counts[func_name] = 0 - self.__dict[func_name] = f - self.__stack.append(f) - - def visit_property_init(self, node): - """ - Public method to treat a property_init node. - - @param node reference to the node (jasy.script.parse.Node.Node) - """ - if node.type == "property_init" and node[1].type == "function": - if self.__stack and self.__stack[-1].endlineno < node[0].line: - del self.__stack[-1] - endline = node[0].line + self.__source.count("\n", node.start, node[1].end) - if getattr(node[1], "params", None): - func_sig = ", ".join([p.value for p in node[1].params]) - else: - func_sig = "" - if self.__stack: - # it's a nested function - cur_func = self.__stack[-1] - f = Function(None, node[0].value, self.__file, node[0].line, func_sig) - f.setEndLine(endline) - cur_func._addmethod(node[0].value, f) - else: - f = Function( - self.__module, node[0].value, self.__file, node[0].line, func_sig - ) - f.setEndLine(endline) - func_name = node[0].value - if func_name in self.__dict_counts: - self.__dict_counts[func_name] += 1 - func_name = "{0}_{1:d}".format( - func_name, self.__dict_counts[func_name] - ) - else: - self.__dict_counts[func_name] = 0 - self.__dict[func_name] = f - self.__stack.append(f) - - def visit_var(self, node): - """ - Public method to treat a variable node. - - @param node reference to the node (jasy.script.parse.Node.Node) - """ - if ( - node.type == "var" - and node.parent.type == "script" - and node.getChildrenLength() - ): - if self.__stack and self.__stack[-1].endlineno < node[0].line: - del self.__stack[-1] - if self.__stack: - # function variables - for var in node: - attr = Attribute(self.__module, var.name, self.__file, var.line) - self.__stack[-1]._addattribute(attr) - else: - # global variable - if "@@Globals@@" not in self.__dict: - self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( - self.__module, "Globals", self.__file, 0 - ) - for var in node: - self.__dict["@@Globals@@"]._addglobal( - Attribute(self.__module, var.name, self.__file, var.line) - ) - - def visit_const(self, node): - """ - Public method to treat a constant node. - - @param node reference to the node (jasy.script.parse.Node.Node) - """ - if ( - node.type == "const" - and node.parent.type == "script" - and node.getChildrenLength() - ): - if self.__stack and self.__stack[-1].endlineno < node[0].line: - del self.__stack[-1] - if self.__stack: - # function variables - for var in node: - attr = Attribute( - self.__module, "const " + var.name, self.__file, var.line - ) - self.__stack[-1]._addattribute(attr) - else: - # global variable - if "@@Globals@@" not in self.__dict: - self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( - self.__module, "Globals", self.__file, 0 - ) - for var in node: - self.__dict["@@Globals@@"]._addglobal( - Attribute( - self.__module, "const " + var.name, self.__file, var.line - ) - ) - - -def readmodule_ex(module, path=None, isTypeFile=False): - """ - Read a JavaScript file and return a dictionary of functions and variables. - - @param module name of the JavaScript file - @type str - @param path path the file should be searched in - @type list of str - @param isTypeFile flag indicating a file of this type - @type bool - @return the resulting dictionary - @rtype dict - """ - # search the path for the file - f = None - fullpath = [] if path is None else path[:] - f, file, (suff, mode, type) = ClassBrowsers.find_module(module, fullpath) - if f: - f.close() - if type not in SUPPORTED_TYPES: - # not Javascript source, can't do anything with this module - return {} - - try: - src = Utilities.readEncodedFile(file)[0] - except (UnicodeError, OSError): - # can't do anything with this module - return {} - - return scan(src, file, module) - - -def scan(src, file, module): - """ - Public method to scan the given source text. - - @param src source text to be scanned - @type str - @param file file name associated with the source text - @type str - @param module module name associated with the source text - @type str - @return dictionary containing the extracted data - @rtype dict - """ - # convert eol markers the Python style - src = src.replace("\r\n", "\n").replace("\r", "\n") - - dictionary = {} - - visitor = Visitor(src, module, file) - dictionary = visitor.parse() - return dictionary