--- a/src/eric7/Utilities/ClassBrowsers/__init__.py Sun Dec 11 18:24:41 2022 +0100 +++ b/src/eric7/Utilities/ClassBrowsers/__init__.py Mon Dec 12 16:35:21 2022 +0100 @@ -18,39 +18,83 @@ </ul> """ -# TODO: move the IDL and Protobuf class browsers to the plugins. - import importlib import os from eric7 import Preferences +# The class browser registry +# Dictionary with class browser name as key. Each entry is a dictionary with +# 0. 'Extensions': list of associated file extensions with leading dot +# 1. 'ReadModule': function to read and parse a module file +# 2. 'Scan': function to parse a given source text +# 3. 'FileIcon': function to get an icon name for the file type +ClassBrowserRegistry = {} + PY_SOURCE = 1 PTL_SOURCE = 128 RB_SOURCE = 129 -IDL_SOURCE = 130 -JS_SOURCE = 131 -PROTO_SOURCE = 132 +JS_SOURCE = 130 UNKNOWN_SOURCE = 255 SUPPORTED_TYPES = [ PY_SOURCE, PTL_SOURCE, RB_SOURCE, - IDL_SOURCE, JS_SOURCE, - PROTO_SOURCE, ] __extensions = { - "IDL": [".idl"], "Python": [".py", ".pyw", ".ptl"], # currently not used "Ruby": [".rb"], "JavaScript": [".js"], - "ProtoBuf": [".proto"], } +def registerClassBrowser(name, readModuleFunc, scanFunc, iconFunc, extensions): + """ + Function to register a class browser type. + + @param name name of the class browser + @type str + @param readModuleFunc function to read and parse a file returning a dictionary + with the parsing result + @type function + @param scanFunc function to scan a given source text returning a dictionary with + the parsing result + @type function + @param iconFunc function returning an icon name for the supported files + @type function + @param extensions list of associated file extensions + @type list of str + @exception KeyError raised if the class browser to be registered is already + """ + global ClassBrowserRegistry + + if name in ClassBrowserRegistry: + raise KeyError('Class Browser "{0}" already registered.'.format(name)) + else: + ClassBrowserRegistry[name] = { + "ReadModule": readModuleFunc, + "Scan": scanFunc, + "FileIcon": iconFunc, + "Extensions": extensions, + } + + +def unregisterClassBrowser(name): + """ + Function to unregister a class browser type. + + @param name name of the class browser + @type str + """ + global ClassBrowserRegistry + + if name in ClassBrowserRegistry: + del ClassBrowserRegistry[name] + + def getClassBrowserModule(moduleType): """ Function to import a class browser module. @@ -61,9 +105,7 @@ @rtype module """ typeMapping = { - "idl": ".idlclbr", "javascript": ".jsclbr", - "protobuf": ".protoclbr", "python": ".pyclbr", "ruby": ".rbclbr", } @@ -95,11 +137,14 @@ ext = os.path.splitext(module)[1].lower() path = [] if path is None else path[:] - if ext in __extensions["IDL"]: - moduleType = "idl" - elif ext in __extensions["ProtoBuf"]: - moduleType = "protobuf" - elif ext in __extensions["Ruby"]: + if not isPyFile: + for classBrowserName in ClassBrowserRegistry: + if ext in ClassBrowserRegistry[classBrowserName]["Extensions"]: + return ClassBrowserRegistry[classBrowserName]["ReadModule"]( + module, path + ) + + if ext in __extensions["Ruby"]: moduleType = "ruby" elif ext in __extensions["JavaScript"]: moduleType = "javascript" @@ -110,11 +155,49 @@ moduleType = "python" classBrowserModule = getClassBrowserModule(moduleType) - if classBrowserModule: - dictionary = classBrowserModule.readmodule_ex(module, path, isTypeFile=isPyFile) - classBrowserModule.clearModulesCache() + dictionary = ( + classBrowserModule.readmodule_ex(module, path, isTypeFile=isPyFile) + if classBrowserModule + else {} + ) + + return dictionary + + +def scan(src, filename, module, isPyFile=False): + """ + Function to scan the given source text. + + @param src source text to be scanned + @type str + @param filename file name associated with the source text + @type str + @param module module name associated with the source text + @type str + @param isPyFile flag indicating a Python file + @type bool + @return dictionary containing the extracted data + @rtype dict + """ + ext = os.path.splitext(filename)[1] + for classBrowserName in ClassBrowserRegistry: + if ext in ClassBrowserRegistry[classBrowserName]["Extensions"]: + return ClassBrowserRegistry[classBrowserName]["Scan"](src, filename, module) + + if ext in __extensions["Ruby"]: + moduleType = "ruby" + elif ext in __extensions["JavaScript"]: + moduleType = "javascript" + elif ext in Preferences.getPython("Python3Extensions") or isPyFile: + moduleType = "python" else: - dictionary = {} + # try Python if it is without extension + moduleType = "python" + + classBrowserModule = getClassBrowserModule(moduleType) + dictionary = ( + classBrowserModule.scan(src, filename, module) if classBrowserModule else None + ) return dictionary @@ -143,10 +226,6 @@ if ext in __extensions["Ruby"]: sourceType = RB_SOURCE - elif ext in __extensions["IDL"]: - sourceType = IDL_SOURCE - elif ext in __extensions["ProtoBuf"]: - sourceType = PROTO_SOURCE elif ext in __extensions["JavaScript"]: sourceType = JS_SOURCE elif ext == ".ptl": @@ -180,3 +259,38 @@ # __IGNORE_WARNING_Y115__ raise ImportError + + +def getIcon(filename): + """ + Function to get an icon name for the given file (only for class browsers provided + via plugins). + + @param filename name of the file + @type str + @return icon name + @rtype str + """ + ext = os.path.splitext(filename)[1].lower() + + for classBrowserRegistryEntry in ClassBrowserRegistry.values(): + if ext in classBrowserRegistryEntry["Extensions"]: + return classBrowserRegistryEntry["FileIcon"](filename) + + return "fileMisc" + + +def isSupportedType(fileext): + """ + Function to check, if the given file extension indicates a supported file type. + + @param fileext file extension + @type str + @return flag indicating a supported file type + @rtype bool + """ + supported = any(fileext in exts for exts in __extensions.values()) + supported |= any( + fileext in cb["Extensions"] for cb in ClassBrowserRegistry.values() + ) + return supported