Adapted the file browser incl. the class browsers to support the 'eric-ide' server. server

Mon, 19 Feb 2024 15:56:51 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 19 Feb 2024 15:56:51 +0100
branch
server
changeset 10592
2bada76be1a6
parent 10591
9acd8da2b378
child 10594
6156d9675f62

Adapted the file browser incl. the class browsers to support the 'eric-ide' server.

src/eric7/UI/Browser.py file | annotate | diff | comparison | revisions
src/eric7/UI/BrowserModel.py file | annotate | diff | comparison | revisions
src/eric7/UI/UserInterface.py file | annotate | diff | comparison | revisions
src/eric7/Utilities/ClassBrowsers/__init__.py file | annotate | diff | comparison | revisions
src/eric7/Utilities/ClassBrowsers/pyclbr.py file | annotate | diff | comparison | revisions
src/eric7/Utilities/ClassBrowsers/rbclbr.py file | annotate | diff | comparison | revisions
--- a/src/eric7/UI/Browser.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/UI/Browser.py	Mon Feb 19 15:56:51 2024 +0100
@@ -35,6 +35,7 @@
 from eric7.EricWidgets import EricFileDialog, EricMessageBox
 from eric7.EricWidgets.EricApplication import ericApp
 from eric7.Project.ProjectBrowserModel import ProjectBrowserSimpleDirectoryItem
+from eric7.RemoteServerInterface import EricServerFileDialog
 from eric7.SystemUtilities import FileSystemUtilities
 from eric7.UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
 from eric7.Utilities import MimeTypes
@@ -106,10 +107,12 @@
     pdfFile = pyqtSignal(str)
     testFile = pyqtSignal(str)
 
-    def __init__(self, parent=None):
+    def __init__(self, serverInterface, parent=None):
         """
         Constructor
 
+        @param serverInterface reference to the 'eric-ide' server interface object
+        @type EricServerInterface
         @param parent parent widget
         @type QWidget
         """
@@ -118,7 +121,10 @@
         self.setWindowTitle(QCoreApplication.translate("Browser", "File-Browser"))
         self.setWindowIcon(EricPixmapCache.getIcon("eric"))
 
-        self.__model = BrowserModel()
+        self.__ericServerInterface = serverInterface
+        self.__remotefsInterface = serverInterface.getServiceInterface("FileSystem")
+
+        self.__model = BrowserModel(fsInterface=self.__remotefsInterface)
         self.__sortModel = BrowserSortFilterProxyModel()
         self.__sortModel.setSourceModel(self.__model)
         self.setModel(self.__sortModel)
@@ -338,15 +344,19 @@
         # create the directory menu
         self.dirMenu = QMenu(self)
         self.dirMenu.addAction(
-            QCoreApplication.translate("Browser", "New toplevel directory..."),
-            self.__newToplevelDir,
+            QCoreApplication.translate("Browser", "New Top Level Directory..."),
+            self.__newTopLevelDir,
+        )
+        self.__dmRemoteTopLevelAct = self.dirMenu.addAction(
+            QCoreApplication.translate("Browser", "New Remote Top Level Directory..."),
+            self.__newRemoteTopLevelDir,
         )
         self.addAsTopLevelAct = self.dirMenu.addAction(
-            QCoreApplication.translate("Browser", "Add as toplevel directory"),
+            QCoreApplication.translate("Browser", "Add as top level directory"),
             self.__addAsToplevelDir,
         )
         self.removeFromToplevelAct = self.dirMenu.addAction(
-            QCoreApplication.translate("Browser", "Remove from toplevel"),
+            QCoreApplication.translate("Browser", "Remove from top level"),
             self.__removeToplevel,
         )
         self.dirMenu.addSeparator()
@@ -387,8 +397,12 @@
 
         self.attributeMenu = QMenu(self)
         self.attributeMenu.addAction(
-            QCoreApplication.translate("Browser", "New toplevel directory..."),
-            self.__newToplevelDir,
+            QCoreApplication.translate("Browser", "New Top Level Directory..."),
+            self.__newTopLevelDir,
+        )
+        self.__amRemoteTopLevelAct = self.attributeMenu.addAction(
+            QCoreApplication.translate("Browser", "New Remote Top Level Directory..."),
+            self.__newRemoteTopLevelDir,
         )
         self.attributeMenu.addSeparator()
         self.attributeMenu.addMenu(self.gotoMenu)
@@ -396,8 +410,12 @@
         # create the background menu
         self.backMenu = QMenu(self)
         self.backMenu.addAction(
-            QCoreApplication.translate("Browser", "New toplevel directory..."),
-            self.__newToplevelDir,
+            QCoreApplication.translate("Browser", "New Top Level Directory..."),
+            self.__newTopLevelDir,
+        )
+        self.__bmRemoteTopLevelAct = self.backMenu.addAction(
+            QCoreApplication.translate("Browser", "New Remote Top Level Directory..."),
+            self.__newRemoteTopLevelDir,
         )
         self.backMenu.addSeparator()
         self.backMenu.addAction(self.showHiddenFilesAct)
@@ -431,7 +449,7 @@
 
     def _contextMenuRequested(self, coord):
         """
-        Protected slot to show the context menu of the listview.
+        Protected slot to show the context menu of the list view.
 
         @param coord the position of the mouse pointer
         @type QPoint
@@ -475,6 +493,9 @@
                     self.openInPdfViewerAct.setVisible(False)
                     self.menu.popup(coord)
                 elif isinstance(itm, BrowserClassAttributeItem):
+                    self.__amRemoteTopLevelAct.setEnabled(
+                        self.__ericServerInterface.isServerConnected()
+                    )
                     self.attributeMenu.popup(coord)
                 elif isinstance(itm, BrowserDirectoryItem):
                     if not index.parent().isValid():
@@ -483,10 +504,19 @@
                     else:
                         self.removeFromToplevelAct.setEnabled(False)
                         self.addAsTopLevelAct.setEnabled(True)
+                    self.__dmRemoteTopLevelAct.setEnabled(
+                        self.__ericServerInterface.isServerConnected()
+                    )
                     self.dirMenu.popup(coord)
                 else:
+                    self.__bmRemoteTopLevelAct.setEnabled(
+                        self.__ericServerInterface.isServerConnected()
+                    )
                     self.backMenu.popup(coord)
             else:
+                self.__bmRemoteTopLevelAct.setEnabled(
+                    self.__ericServerInterface.isServerConnected()
+                )
                 self.backMenu.popup(self.mapToGlobal(coord))
 
     def _showGotoMenu(self):
@@ -755,6 +785,7 @@
         # remember the current state
         Preferences.setUI("BrowsersListHiddenFiles", checked)
 
+    @pyqtSlot()
     def handleTesting(self):
         """
         Public slot to handle the testing popup menu entry.
@@ -769,13 +800,14 @@
         if pyfn is not None:
             self.testFile.emit(pyfn)
 
-    def __newToplevelDir(self):
+    @pyqtSlot()
+    def __newTopLevelDir(self):
         """
-        Private slot to handle the New toplevel directory popup menu entry.
+        Private slot to handle the New Top Level Directory popup menu entry.
         """
         dname = EricFileDialog.getExistingDirectory(
             None,
-            QCoreApplication.translate("Browser", "New toplevel directory"),
+            QCoreApplication.translate("Browser", "New Top Level Directory"),
             "",
             EricFileDialog.ShowDirsOnly,
         )
@@ -783,22 +815,39 @@
             dname = os.path.abspath(FileSystemUtilities.toNativeSeparators(dname))
             self.__model.addTopLevelDir(dname)
 
+    @pyqtSlot()
+    def __newRemoteTopLevelDir(self):
+        """
+        Private slot to handle the New Remote Top Level Directory popup menu entry.
+        """
+        dname = EricServerFileDialog.getExistingDirectory(
+            None,
+            QCoreApplication.translate("Browser", "New Remote Top Level Directory"),
+            "",
+            dirsOnly=True,
+        )
+        if dname:
+            self.__model.addTopLevelDir(dname)
+
+    @pyqtSlot()
     def __removeToplevel(self):
         """
-        Private slot to handle the Remove from toplevel popup menu entry.
+        Private slot to handle the Remove from top level popup menu entry.
         """
         index = self.currentIndex()
         sindex = self.model().mapToSource(index)
         self.__model.removeToplevelDir(sindex)
 
+    @pyqtSlot()
     def __addAsToplevelDir(self):
         """
-        Private slot to handle the Add as toplevel directory popup menu entry.
+        Private slot to handle the Add as top level directory popup menu entry.
         """
         index = self.currentIndex()
         dname = self.model().item(index).dirName()
         self.__model.addTopLevelDir(dname)
 
+    @pyqtSlot()
     def __refreshDirectory(self):
         """
         Private slot to refresh a directory entry.
@@ -807,6 +856,7 @@
         refreshDir = self.model().item(index).dirName()
         self.__model.directoryChanged(refreshDir)
 
+    @pyqtSlot()
     def __findInDirectory(self):
         """
         Private slot to handle the Find in directory popup menu entry.
@@ -816,6 +866,7 @@
 
         ericApp().getObject("UserInterface").showFindFilesWidget(searchDir=searchDir)
 
+    @pyqtSlot()
     def __replaceInDirectory(self):
         """
         Private slot to handle the Find&Replace in directory popup menu entry.
@@ -825,6 +876,7 @@
 
         ericApp().getObject("UserInterface").showReplaceFilesWidget(searchDir=searchDir)
 
+    @pyqtSlot(str)
     def handleProgramChange(self, fn):
         """
         Public slot to handle the programChange signal.
@@ -834,6 +886,7 @@
         """
         self.__model.programChange(os.path.dirname(fn))
 
+    @pyqtSlot(str)
     def handleInterpreterChanged(self, interpreter):
         """
         Public slot to handle a change of the debug client's interpreter.
--- a/src/eric7/UI/BrowserModel.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/UI/BrowserModel.py	Mon Feb 19 15:56:51 2024 +0100
@@ -62,14 +62,17 @@
     Class implementing the browser model.
     """
 
-    def __init__(self, parent=None, nopopulate=False):
+    def __init__(self, parent=None, nopopulate=False, fsInterface=None):
         """
         Constructor
 
-        @param parent reference to parent object
-        @type QObject
-        @param nopopulate flag indicating to not populate the model
-        @type bool
+        @param parent reference to parent object (defaults to None)
+        @type QObject (optional)
+        @param nopopulate flag indicating to not populate the model (defaults to False)
+        @type bool (optional)
+        @param fsInterface reference to the 'eric-ide' server interface object
+            (defaults to None)
+        @type EricServerFileSystemInterface (optional)
         """
         super().__init__(parent)
 
@@ -78,6 +81,8 @@
         self.__sysPathInterpreter = ""
         self.__sysPathItem = None
 
+        self.__remotefsInterface = fsInterface
+
         if not nopopulate:
             self.watchedItems = {}
             self.watchedFileItems = {}
@@ -326,8 +331,8 @@
             dirName = itm.dirName()
             if (
                 dirName != ""
-                and not dirName.startswith("//")
-                and not dirName.startswith("\\\\")
+                and not FileSystemUtilities.isRemoteFileName(dirName)
+                and not dirName.startswith(("//", "\\\\"))
             ):
                 if dirName not in self.watcher.directories():
                     self.watcher.addPath(dirName)
@@ -442,7 +447,9 @@
                 )
 
         for d in self.toplevelDirs:
-            itm = BrowserDirectoryItem(self.rootItem, d)
+            itm = BrowserDirectoryItem(
+                self.rootItem, d, fsInterface=self.__remotefsInterface
+            )
             self._addItem(itm, self.rootItem)
 
     def interpreterChanged(self, interpreter):
@@ -502,7 +509,9 @@
             self.endRemoveRows()
             self.progDir = None
 
-        itm = BrowserDirectoryItem(self.rootItem, dirname)
+        itm = BrowserDirectoryItem(
+            self.rootItem, dirname, fsInterface=self.__remotefsInterface
+        )
         self.addItem(itm)
         self.progDir = itm
 
@@ -514,7 +523,9 @@
         @type str
         """
         if dirname not in self.toplevelDirs:
-            itm = BrowserDirectoryItem(self.rootItem, dirname)
+            itm = BrowserDirectoryItem(
+                self.rootItem, dirname, fsInterface=self.__remotefsInterface
+            )
             self.addItem(itm)
             self.toplevelDirs.append(itm.dirName())
 
@@ -607,39 +618,74 @@
         """
         self._addWatchedItem(parentItem)
 
-        qdir = QDir(parentItem.dirName())
+        # TODO: add support for 'remote:' directories
+        dirName = parentItem.dirName()
+        if FileSystemUtilities.isPlainFileName(dirName):
+            qdir = QDir(dirName)
 
-        dirFilter = (
-            QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden
-        )
-        entryInfoList = qdir.entryInfoList(dirFilter)
-        if len(entryInfoList) > 0:
-            if repopulate:
-                self.beginInsertRows(
-                    self.createIndex(parentItem.row(), 0, parentItem),
-                    0,
-                    len(entryInfoList) - 1,
-                )
-            for f in entryInfoList:
-                if f.isDir():
-                    node = BrowserDirectoryItem(
-                        parentItem,
-                        FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
-                        False,
+            dirFilter = (
+                QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden
+            )
+            entryInfoList = qdir.entryInfoList(dirFilter)
+            if len(entryInfoList) > 0:
+                if repopulate:
+                    self.beginInsertRows(
+                        self.createIndex(parentItem.row(), 0, parentItem),
+                        0,
+                        len(entryInfoList) - 1,
                     )
-                else:
-                    fileFilters = Preferences.getUI("BrowsersFileFilters").split(";")
-                    if fileFilters:
-                        fn = f.fileName()
-                        if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters):
-                            continue
-                    node = BrowserFileItem(
-                        parentItem,
-                        FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
+                for f in entryInfoList:
+                    if f.isDir():
+                        node = BrowserDirectoryItem(
+                            parentItem,
+                            FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
+                            False,
+                        )
+                    else:
+                        fileFilters = Preferences.getUI("BrowsersFileFilters").split(";")
+                        if fileFilters:
+                            fn = f.fileName()
+                            if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters):
+                                continue
+                        node = BrowserFileItem(
+                            parentItem,
+                            FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
+                        )
+                    self._addItem(node, parentItem)
+                if repopulate:
+                    self.endInsertRows()
+
+        elif FileSystemUtilities.isRemoteFileName(dirName):
+            entriesList = self.__remotefsInterface.listdir(dirName)[2]
+            if len(entriesList) > 0:
+                if repopulate:
+                    self.beginInsertRows(
+                        self.createIndex(parentItem.row(), 0, parentItem),
+                        0,
+                        len(entryInfoList) - 1,
                     )
-                self._addItem(node, parentItem)
-            if repopulate:
-                self.endInsertRows()
+                for entry in entriesList:
+                    if entry["is_dir"]:
+                        node = BrowserDirectoryItem(
+                            parentItem,
+                            FileSystemUtilities.remoteFileName(entry["path"]),
+                            False,
+                            fsInterface=self.__remotefsInterface,
+                        )
+                    else:
+                        fileFilters = Preferences.getUI("BrowsersFileFilters").split(";")
+                        if fileFilters:
+                            fn = entry["name"]
+                            if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters):
+                                continue
+                        node = BrowserFileItem(
+                            parentItem,
+                            FileSystemUtilities.remoteFileName(entry["path"]),
+                            fsInterface=self.__remotefsInterface,
+                        )
+                    self._addItem(node, parentItem)
+                if repopulate:
+                    self.endInsertRows()
 
     def populateSysPathItem(self, parentItem, repopulate=False):
         """
@@ -692,6 +738,7 @@
         """
         from eric7.Utilities import ClassBrowsers
 
+        # TODO: add support for 'remote:' directories
         moduleName = parentItem.moduleName()
         fileName = parentItem.fileName()
         try:
@@ -721,11 +768,16 @@
                 cl = dictionary[key]
                 with contextlib.suppress(AttributeError):
                     if cl.module == moduleName:
-                        if isinstance(cl, ClbrBaseClasses.Class):
+                        if isinstance(
+                            cl, (ClbrBaseClasses.Class, ClbrBaseClasses.Module)
+                        ):
                             node = BrowserClassItem(parentItem, cl, fileName)
                         elif isinstance(cl, ClbrBaseClasses.Function):
                             node = BrowserMethodItem(parentItem, cl, fileName)
-                        self._addItem(node, parentItem)
+                        else:
+                            node = None
+                        if node:
+                            self._addItem(node, parentItem)
             if "@@Coding@@" in dictionary and Preferences.getUI("BrowserShowCoding"):
                 node = BrowserCodingItem(
                     parentItem,
@@ -1147,6 +1199,7 @@
     Class implementing the data structure for browser simple directory items.
     """
 
+    # TODO: add support for 'remote:' directories
     def __init__(self, parent, text, path=""):
         """
         Constructor
@@ -1181,6 +1234,7 @@
         @param full flag indicating full path name should be displayed
         @type bool
         """
+        # TODO: add support for 'remote:' directories
         self._dirName = os.path.abspath(dinfo)
         self.itemData[0] = os.path.basename(self._dirName)
 
@@ -1228,7 +1282,7 @@
     Class implementing the data structure for browser directory items.
     """
 
-    def __init__(self, parent, dinfo, full=True):
+    def __init__(self, parent, dinfo, full=True, fsInterface=None):
         """
         Constructor
 
@@ -1236,21 +1290,37 @@
         @type BrowserItem
         @param dinfo dinfo is the string for the directory
         @type str
-        @param full flag indicating full pathname should be displayed
-        @type bool
+        @param full flag indicating full pathname should be displayed (defaults to True)
+        @type bool (optional)
+        @param fsInterface reference to the 'eric-ide' server file system interface
+            (defaults to None)
+        @type EricServerFileSystemInterface (optional)
         """
-        self._dirName = os.path.abspath(dinfo)
-        dn = self._dirName if full else os.path.basename(self._dirName)
+        self.__fsInterface = fsInterface
+
+        if FileSystemUtilities.isRemoteFileName(dinfo):
+            self._dirName = dinfo
+            dn = (
+                self._dirName
+                if full
+                else self.__fsInterface.basename(self._dirName)
+            )
+        else:
+            self._dirName = os.path.abspath(dinfo)
+            dn = self._dirName if full else os.path.basename(self._dirName)
         BrowserItem.__init__(self, parent, dn)
 
         self.type_ = BrowserItemType.Directory
         if (
-            not FileSystemUtilities.isDrive(self._dirName)
+            FileSystemUtilities.isPlainFileName(self._dirName)
+            and not FileSystemUtilities.isDrive(self._dirName)
             and os.path.lexists(self._dirName)
             and os.path.islink(self._dirName)
         ):
             self.symlink = True
             self.icon = EricPixmapCache.getSymlinkIcon("dirClosed")
+        elif FileSystemUtilities.isRemoteFileName(self._dirName):
+            self.icon = EricPixmapCache.getIcon("open-remote")
         else:
             self.icon = EricPixmapCache.getIcon("dirClosed")
         self._populated = False
@@ -1265,8 +1335,16 @@
         @param full flag indicating full pathname should be displayed
         @type bool
         """
-        self._dirName = os.path.abspath(dinfo)
-        dn = self._dirName if full else os.path.basename(self._dirName)
+        if FileSystemUtilities.isRemoteFileName(dinfo):
+            self._dirName = dinfo
+            dn = (
+                self._dirName
+                if full
+                else self.__fsInterface.basename(self._dirName)
+            )
+        else:
+            self._dirName = os.path.abspath(dinfo)
+            dn = self._dirName if full else os.path.basename(self._dirName)
         self.itemData[0] = dn
 
     def dirName(self):
@@ -1337,12 +1415,13 @@
         return "sys.path"
 
 
+# TODO: add support for 'remote:' directories
 class BrowserFileItem(BrowserItem):
     """
     Class implementing the data structure for browser file items.
     """
 
-    def __init__(self, parent, finfo, full=True, sourceLanguage=""):
+    def __init__(self, parent, finfo, full=True, sourceLanguage="", fsInterface=None):
         """
         Constructor
 
@@ -1350,17 +1429,29 @@
         @type BrowserItem
         @param finfo the string for the file
         @type str
-        @param full flag indicating full pathname should be displayed
-        @type bool
-        @param sourceLanguage source code language of the project
-        @type str
+        @param full flag indicating full pathname should be displayed (defaults to True)
+        @type bool (optional)
+        @param sourceLanguage source code language of the project (defaults to "")
+        @type str (optional)
+        @param fsInterface reference to the 'eric-ide' server file system interface
+            (defaults to None)
+        @type EricServerFileSystemInterface (optional)
         """
-        BrowserItem.__init__(self, parent, os.path.basename(finfo))
+        self.__fsInterface = fsInterface
 
+        if FileSystemUtilities.isRemoteFileName(finfo):
+            dirname, basename = self.__fsInterface.split(finfo)
+            self.fileext = self.__fsInterface.splitext(finfo)[1].lower()
+            self._filename = finfo
+        else:
+            dirname, basename = os.path.split(finfo)
+            self.fileext = os.path.splitext(finfo)[1].lower()
+            self._filename = os.path.abspath(finfo)
+
+        BrowserItem.__init__(self, parent, basename)
+
+        self._dirName = dirname
         self.type_ = BrowserItemType.File
-        self.fileext = os.path.splitext(finfo)[1].lower()
-        self._filename = os.path.abspath(finfo)
-        self._dirName = os.path.dirname(finfo)
         self.sourceLanguage = sourceLanguage
 
         self._moduleName = ""
@@ -1370,17 +1461,17 @@
             pixName = "filePython"
             self._populated = False
             self._lazyPopulation = True
-            self._moduleName = os.path.basename(finfo)
+            self._moduleName = basename
         elif self.isCythonFile():
             pixName = "lexerCython"
             self._populated = False
             self._lazyPopulation = True
-            self._moduleName = os.path.basename(finfo)
+            self._moduleName = basename
         elif self.isRubyFile():
             pixName = "fileRuby"
             self._populated = False
             self._lazyPopulation = True
-            self._moduleName = os.path.basename(finfo)
+            self._moduleName = basename
         elif self.isDesignerFile():
             pixName = "fileDesigner"
         elif self.isLinguistFile():
@@ -1404,18 +1495,22 @@
             pixName = "fileJavascript"
             self._populated = False
             self._lazyPopulation = True
-            self._moduleName = os.path.basename(finfo)
+            self._moduleName = basename
         elif self.isEricGraphicsFile():
             pixName = "fileUML"
         elif self.isParsableFile():
             pixName = ClassBrowsers.getIcon(self._filename)
             self._populated = False
             self._lazyPopulation = True
-            self._moduleName = os.path.basename(finfo)
+            self._moduleName = basename
         else:
             pixName = "fileMisc"
 
-        if os.path.lexists(self._filename) and os.path.islink(self._filename):
+        if (
+            FileSystemUtilities.isPlainFileName(self._filename)
+            and os.path.lexists(self._filename)
+            and os.path.islink(self._filename)
+        ):
             self.symlink = True
             self.icon = EricPixmapCache.getSymlinkIcon(pixName)
         else:
@@ -1430,12 +1525,25 @@
         @param full flag indicating full pathname should be displayed
         @type bool
         """
-        self._filename = os.path.abspath(finfo)
-        self.itemData[0] = os.path.basename(finfo)
-        self.fileext = os.path.splitext(finfo)[1].lower()
-        if self.isPython3File() or self.isRubyFile() or self.isParsableFile():
-            self._dirName = os.path.dirname(finfo)
-            self._moduleName = os.path.basename(finfo)
+        if FileSystemUtilities.isRemoteFileName(finfo):
+            dirname, basename = self.__fsInterface.split(finfo)
+            self.fileext = self.__fsInterface.splitext(finfo)[1].lower()
+            self._filename = finfo
+        else:
+            dirname, basename = os.path.split(finfo)
+            self.fileext = os.path.splitext(finfo)[1].lower()
+            self._filename = os.path.abspath(finfo)
+
+        self.itemData[0] = basename
+        if (
+            self.isPython3File()
+            or self.isCythonFile()
+            or self.isRubyFile()
+            or self.isJavaScriptFile()
+            or self.isParsableFile()
+        ):
+            self._dirName = dirname
+            self._moduleName = basename
 
     def fileName(self):
         """
@@ -1649,8 +1757,18 @@
             return order == Qt.SortOrder.DescendingOrder
 
         if issubclass(other.__class__, BrowserFileItem):
-            sinit = os.path.basename(self._filename).startswith("__init__.py")
-            oinit = os.path.basename(other.fileName()).startswith("__init__.py")
+            if FileSystemUtilities.isRemoteFileName(self._filename):
+                basename = self.__fsInterface.basename(self._filename)
+            else:
+                basename = os.path.basename(self._filename)
+            sinit = basename.startswith("__init__.py")
+
+            if FileSystemUtilities.isRemoteFileName(other.fileName()):
+                basename = self.__fsInterface.basename(other.fileName())
+            else:
+                basename = os.path.basename(other.fileName())
+            oinit = basename.startswith("__init__.py")
+
             if sinit and not oinit:
                 return order == Qt.SortOrder.AscendingOrder
             if not sinit and oinit:
--- a/src/eric7/UI/UserInterface.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/UI/UserInterface.py	Mon Feb 19 15:56:51 2024 +0100
@@ -394,9 +394,6 @@
         self.stdout = Redirector(False, self)
         self.stderr = Redirector(True, self)
 
-        # create the remote server interface
-        self.__ericServerInterface = EricServerInterface(self)
-
         # set a few dialog members for non-modal dialogs created on demand
         self.programsDialog = None
         self.shortcutsDialog = None
@@ -807,6 +804,11 @@
         from .PythonAstViewer import PythonAstViewer
         from .PythonDisViewer import PythonDisViewer
 
+
+        # create the remote server interface
+        logging.debug("Creating 'eric-ide' Server Interface...")
+        self.__ericServerInterface = EricServerInterface(self)
+
         # Create the view manager depending on the configuration setting
         logging.debug("Creating Viewmanager...")
         self.viewmanager = ViewManager.factory(
@@ -867,7 +869,7 @@
             logging.debug("Creating File Browser...")
             from .Browser import Browser  # noqa: I101
 
-            self.browser = Browser()
+            self.browser = Browser(self.__ericServerInterface)
         else:
             logging.debug("File Browser disabled")
             self.browser = None
--- a/src/eric7/Utilities/ClassBrowsers/__init__.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/Utilities/ClassBrowsers/__init__.py	Mon Feb 19 15:56:51 2024 +0100
@@ -108,7 +108,7 @@
     return None
 
 
-def readmodule(module, path=None, isPyFile=False):
+def readmodule(module, searchPath=None, isPyFile=False):
     """
     Function to read a source file and return a dictionary of classes, functions,
     modules, etc. .
@@ -118,7 +118,7 @@
 
     @param module name of the source file
     @type str
-    @param path list of paths the file should be searched in
+    @param searchPath list of paths the file should be searched in
     @type list of str
     @param isPyFile flag indicating a Python file
     @type bool
@@ -126,13 +126,13 @@
     @rtype dict
     """
     ext = os.path.splitext(module)[1].lower()
-    path = [] if path is None else path[:]
+    searchPath = [] if searchPath is None else searchPath[:]
 
     if not isPyFile:
         for classBrowserName in ClassBrowserRegistry:
             if ext in ClassBrowserRegistry[classBrowserName]["Extensions"]:
                 return ClassBrowserRegistry[classBrowserName]["ReadModule"](
-                    module, path
+                    module, searchPath
                 )
 
     if ext in __extensions["Ruby"]:
@@ -145,7 +145,7 @@
 
     classBrowserModule = getClassBrowserModule(moduleType)
     dictionary = (
-        classBrowserModule.readmodule_ex(module, path, isTypeFile=isPyFile)
+        classBrowserModule.readmodule_ex(module, searchPath, isTypeFile=isPyFile)
         if classBrowserModule
         else {}
     )
@@ -244,6 +244,34 @@
     raise ImportError
 
 
+def determineSourceType(name, isPyFile=False):
+    """
+    Function to determine the type of a source file given its name.
+
+    @param name file name or module name
+    @type str
+    @param isPyFile flag indicating a Python file (defaults to False)
+    @type bool (optional)
+    @return source file type
+    @rtype int
+    """
+    ext = os.path.splitext(name)[1].lower()
+
+    if ext in __extensions["Ruby"]:
+        sourceType = RB_SOURCE
+    elif ext == ".ptl":
+        sourceType = PTL_SOURCE
+    elif (
+        name.lower().endswith(tuple(Preferences.getPython("Python3Extensions")))
+        or isPyFile
+    ):
+        sourceType = PY_SOURCE
+    else:
+        sourceType = UNKNOWN_SOURCE
+
+    return sourceType
+
+
 def getIcon(filename):
     """
     Function to get an icon name for the given file (only for class browsers provided
--- a/src/eric7/Utilities/ClassBrowsers/pyclbr.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/Utilities/ClassBrowsers/pyclbr.py	Mon Feb 19 15:56:51 2024 +0100
@@ -20,6 +20,8 @@
 from PyQt6.QtCore import QRegularExpression
 
 from eric7 import Utilities
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.SystemUtilities import FileSystemUtilities
 from eric7.Utilities import ClassBrowsers
 
 from . import ClbrBaseClasses
@@ -395,7 +397,7 @@
                 self.importedNames[name].append(lineno)
 
 
-def readmodule_ex(module, path=None, isTypeFile=False):
+def readmodule_ex(module, searchPath=None, isTypeFile=False):
     """
     Read a module file and return a dictionary of classes.
 
@@ -405,29 +407,37 @@
 
     @param module name of the module file
     @type str
-    @param path path the module should be searched in
+    @param searchPath path the module 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 module
-    path = [] if path is None else path[:]
-    f = None
-    if f is None:
-        fullpath = path[:] + sys.path[:]
+    fsInterface = ericApp().getObject("EricServer").getServiceInterface("FileSystem")
+
+    if searchPath and FileSystemUtilities.isRemoteFileName(searchPath[0]):
+        type = ClassBrowsers.determineSourceType(module, isTypeFile)
+        file = fsInterface.join(searchPath[0], module)
+    else:
+        # search the path for the module
+        searchPath = [] if searchPath is None else searchPath[:]
+        fullpath = searchPath[:] + sys.path[:]
         f, file, (suff, mode, type) = ClassBrowsers.find_module(
             module, fullpath, isTypeFile
         )
-    if f:
-        f.close()
+        if f:
+            f.close()
+
     if type not in SUPPORTED_TYPES:
         # not Python source, can't do anything with this module
         return {}
 
     try:
-        src = Utilities.readEncodedFile(file)[0]
+        if FileSystemUtilities.isRemoteFileName(file):
+            src = fsInterface.readEncodedFile(file)[0]
+        else:
+            src = Utilities.readEncodedFile(file)[0]
     except (OSError, UnicodeError):
         # can't do anything with this module
         return {}
--- a/src/eric7/Utilities/ClassBrowsers/rbclbr.py	Mon Feb 19 15:34:54 2024 +0100
+++ b/src/eric7/Utilities/ClassBrowsers/rbclbr.py	Mon Feb 19 15:56:51 2024 +0100
@@ -17,6 +17,8 @@
 from PyQt6.QtCore import QRegularExpression
 
 from eric7 import Utilities
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.SystemUtilities import FileSystemUtilities
 from eric7.Utilities import ClassBrowsers
 
 from . import ClbrBaseClasses
@@ -277,31 +279,40 @@
         self.setPrivate()
 
 
-def readmodule_ex(module, path=None, isTypeFile=False):  # noqa: U100
+def readmodule_ex(module, searchPath=None, isTypeFile=False):  # noqa: U100
     """
     Read a Ruby file and return a dictionary of classes, functions and modules.
 
     @param module name of the Ruby file
     @type str
-    @param path path the file should be searched in
+    @param searchPath 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()
+    fsInterface = ericApp().getObject("EricServer").getServiceInterface("FileSystem")
+
+    if searchPath and FileSystemUtilities.isRemoteFileName(searchPath[0]):
+        type = ClassBrowsers.determineSourceType(module)
+        file = fsInterface.join(searchPath[0], module)
+    else:
+        # search the path for the module
+        fullpath = [] if searchPath is None else searchPath[:]
+        f, file, (suff, mode, type) = ClassBrowsers.find_module(module, fullpath)
+        if f:
+            f.close()
+
     if type not in SUPPORTED_TYPES:
         # not Ruby source, can't do anything with this module
         return {}
 
     try:
-        src = Utilities.readEncodedFile(file)[0]
+        if FileSystemUtilities.isRemoteFileName(file):
+            src = fsInterface.readEncodedFile(file)[0]
+        else:
+            src = Utilities.readEncodedFile(file)[0]
     except (OSError, UnicodeError):
         # can't do anything with this module
         return {}

eric ide

mercurial