src/eric7/UI/BrowserModel.py

branch
server
changeset 10592
2bada76be1a6
parent 10517
aecd5a8c958c
child 10594
6156d9675f62
--- 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:

eric ide

mercurial