--- 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: