eric7/QScintilla/EditorOutlineModel.py

branch
eric7
changeset 8312
800c432b34c8
parent 8243
cc717c2ae956
child 8318
962bce857696
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/QScintilla/EditorOutlineModel.py	Sat May 15 18:45:04 2021 +0200
@@ -0,0 +1,277 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the editor outline model.
+"""
+
+import os
+import contextlib
+
+from PyQt5.QtCore import QCoreApplication, QModelIndex
+
+from UI.BrowserModel import (
+    BrowserModel, BrowserItem, BrowserClassItem, BrowserCodingItem,
+    BrowserGlobalsItem, BrowserImportsItem, BrowserImportItem,
+    BrowserClassAttributesItem, BrowserMethodItem
+)
+
+import Preferences
+
+
+class EditorOutlineModel(BrowserModel):
+    """
+    Class implementing the editor outline model.
+    """
+    SupportedLanguages = (
+        "IDL", "JavaScript", "Protocol", "Python3", "MicroPython", "Cython",
+        "Ruby",
+    )
+    
+    def __init__(self, editor, populate=True):
+        """
+        Constructor
+        
+        @param editor reference to the editor containing the source text
+        @type Editor
+        @param populate flag indicating to populate the outline
+        @type bool
+        """
+        super().__init__(nopopulate=True)
+        
+        self.__editor = editor
+        
+        self.__populated = False
+        
+        rootData = QCoreApplication.translate("EditorOutlineModel", "Name")
+        self.rootItem = BrowserItem(None, rootData)
+        
+        if populate:
+            self.__populateModel()
+    
+    def __populateModel(self, repopulate=False):
+        """
+        Private slot to populate the model.
+        
+        @param repopulate flag indicating a repopulation
+        @type bool
+        """
+        self.__filename = self.__editor.getFileName()
+        self.__module = os.path.basename(self.__filename)
+        
+        language = self.__editor.getLanguage()
+        if language in EditorOutlineModel.SupportedLanguages:
+            if language == "IDL":
+                from Utilities.ClassBrowsers import idlclbr
+                dictionary = idlclbr.scan(
+                    self.__editor.text(), self.__filename, self.__module)
+                idlclbr._modules.clear()
+            elif language == "Protocol":
+                from Utilities.ClassBrowsers import protoclbr
+                dictionary = protoclbr.scan(
+                    self.__editor.text(), self.__filename, self.__module)
+                protoclbr._modules.clear()
+            elif language == "Ruby":
+                from Utilities.ClassBrowsers import rbclbr
+                dictionary = rbclbr.scan(
+                    self.__editor.text(), self.__filename, self.__module)
+                rbclbr._modules.clear()
+            elif language == "JavaScript":
+                from Utilities.ClassBrowsers import jsclbr
+                dictionary = jsclbr.scan(
+                    self.__editor.text(), self.__filename, self.__module)
+                jsclbr._modules.clear()
+            elif language in ("Python3", "MicroPython", "Cython"):
+                from Utilities.ClassBrowsers import pyclbr
+                dictionary = pyclbr.scan(
+                    self.__editor.text(), self.__filename, self.__module)
+                pyclbr._modules.clear()
+            
+            keys = list(dictionary.keys())
+            if len(keys) > 0:
+                parentItem = self.rootItem
+                
+                if repopulate:
+                    last = len(keys) - 1
+                    if (
+                        "@@Coding@@" in keys and
+                        not Preferences.getEditor("SourceOutlineShowCoding")
+                    ):
+                        last -= 1
+                    self.beginInsertRows(QModelIndex(), 0, last)
+                
+                for key in keys:
+                    if key.startswith("@@"):
+                        # special treatment done later
+                        continue
+                    cl = dictionary[key]
+                    with contextlib.suppress(AttributeError):
+                        if cl.module == self.__module:
+                            node = BrowserClassItem(
+                                parentItem, cl, self.__filename)
+                            self._addItem(node, parentItem)
+                if (
+                    "@@Coding@@" in keys and
+                    Preferences.getEditor("SourceOutlineShowCoding")
+                ):
+                    node = BrowserCodingItem(
+                        parentItem,
+                        QCoreApplication.translate(
+                            "EditorOutlineModel", "Coding: {0}")
+                        .format(dictionary["@@Coding@@"].coding),
+                        dictionary["@@Coding@@"].linenumber)
+                    self._addItem(node, parentItem)
+                if "@@Globals@@" in keys:
+                    node = BrowserGlobalsItem(
+                        parentItem,
+                        dictionary["@@Globals@@"].globals,
+                        QCoreApplication.translate(
+                            "EditorOutlineModel", "Globals"))
+                    self._addItem(node, parentItem)
+                if "@@Import@@" in keys or "@@ImportFrom@@" in keys:
+                    node = BrowserImportsItem(
+                        parentItem,
+                        QCoreApplication.translate(
+                            "EditorOutlineModel", "Imports"))
+                    self._addItem(node, parentItem)
+                    if "@@Import@@" in keys:
+                        for importedModule in (
+                            dictionary["@@Import@@"].getImports().values()
+                        ):
+                            m_node = BrowserImportItem(
+                                node,
+                                importedModule.importedModuleName,
+                                importedModule.file,
+                                importedModule.linenos)
+                            self._addItem(m_node, node)
+                            for importedName, linenos in (
+                                importedModule.importedNames.items()
+                            ):
+                                mn_node = BrowserImportItem(
+                                    m_node,
+                                    importedName,
+                                    importedModule.file,
+                                    linenos,
+                                    isModule=False)
+                                self._addItem(mn_node, m_node)
+                if repopulate:
+                    self.endInsertRows()
+            
+            self.__populated = True
+        else:
+            self.clear()
+            self.__populated = False
+    
+    def isPopulated(self):
+        """
+        Public method to check, if the model is populated.
+        
+        @return flag indicating a populated model
+        @rtype bool
+        """
+        return self.__populated
+    
+    def repopulate(self):
+        """
+        Public slot to repopulate the model.
+        """
+        self.clear()
+        self.__populateModel(repopulate=True)
+    
+    def editor(self):
+        """
+        Public method to retrieve a reference to the editor.
+        
+        @return reference to the editor
+        @rtype Editor
+        """
+        return self.__editor
+    
+    def fileName(self):
+        """
+        Public method to retrieve the file name of the editor.
+        
+        @return file name of the editor
+        @rtype str
+        """
+        return self.__filename
+    
+    def itemIndexByLine(self, lineno):
+        """
+        Public method to find an item's index given a line number.
+        
+        @param lineno one based line number of the item
+        @type int
+        @return index of the item found
+        @rtype QModelIndex
+        """
+        def findItem(lineno, parent):
+            """
+            Function to iteratively search for an item containing the given
+            line.
+            
+            @param lineno one based line number of the item
+            @type int
+            @param parent reference to the parent item
+            @type BrowserItem
+            @return found item or None
+            @rtype BrowserItem
+            """
+            if not parent.isPopulated():
+                if parent.isLazyPopulated():
+                    self.populateItem(parent)
+                else:
+                    return None
+            for child in parent.children():
+                if isinstance(child, BrowserClassAttributesItem):
+                    itm = findItem(lineno, child)
+                    if itm is not None:
+                        return itm
+                elif isinstance(child, (BrowserClassItem, BrowserMethodItem)):
+                    start, end = child.boundaries()
+                    if end == -1:
+                        end = 1000000   # assume end of file
+                    if start <= lineno <= end:
+                        itm = findItem(lineno, child)
+                        if itm is not None:
+                            return itm
+                        else:
+                            return child
+                elif hasattr(child, "linenos"):
+                    if lineno in child.linenos():
+                        return child
+                elif (
+                    hasattr(child, "lineno") and
+                    lineno == child.lineno()
+                ):
+                    return child
+            else:
+                return None
+        
+        if self.__populated:
+            for rootChild in self.rootItem.children():
+                itm = None
+                if isinstance(rootChild, BrowserClassItem):
+                    start, end = rootChild.boundaries()
+                    if end == -1:
+                        end = 1000000   # assume end of file
+                    if start <= lineno <= end:
+                        itm = findItem(lineno, rootChild)
+                        if itm is None:
+                            itm = rootChild
+                elif isinstance(rootChild,
+                                (BrowserImportsItem, BrowserGlobalsItem)):
+                    itm = findItem(lineno, rootChild)
+                elif (
+                    isinstance(rootChild, BrowserCodingItem) and
+                    lineno == rootChild.lineno()
+                ):
+                    itm = rootChild
+                if itm is not None:
+                    return self.createIndex(itm.row(), 0, itm)
+            else:
+                return QModelIndex()
+        
+        return QModelIndex()

eric ide

mercurial