60 class BrowserModel(QAbstractItemModel): |
60 class BrowserModel(QAbstractItemModel): |
61 """ |
61 """ |
62 Class implementing the browser model. |
62 Class implementing the browser model. |
63 """ |
63 """ |
64 |
64 |
65 def __init__(self, parent=None, nopopulate=False): |
65 def __init__(self, parent=None, nopopulate=False, fsInterface=None): |
66 """ |
66 """ |
67 Constructor |
67 Constructor |
68 |
68 |
69 @param parent reference to parent object |
69 @param parent reference to parent object (defaults to None) |
70 @type QObject |
70 @type QObject (optional) |
71 @param nopopulate flag indicating to not populate the model |
71 @param nopopulate flag indicating to not populate the model (defaults to False) |
72 @type bool |
72 @type bool (optional) |
|
73 @param fsInterface reference to the 'eric-ide' server interface object |
|
74 (defaults to None) |
|
75 @type EricServerFileSystemInterface (optional) |
73 """ |
76 """ |
74 super().__init__(parent) |
77 super().__init__(parent) |
75 |
78 |
76 self.progDir = None |
79 self.progDir = None |
77 |
80 |
78 self.__sysPathInterpreter = "" |
81 self.__sysPathInterpreter = "" |
79 self.__sysPathItem = None |
82 self.__sysPathItem = None |
|
83 |
|
84 self.__remotefsInterface = fsInterface |
80 |
85 |
81 if not nopopulate: |
86 if not nopopulate: |
82 self.watchedItems = {} |
87 self.watchedItems = {} |
83 self.watchedFileItems = {} |
88 self.watchedFileItems = {} |
84 self.watcher = QFileSystemWatcher(self) |
89 self.watcher = QFileSystemWatcher(self) |
324 """ |
329 """ |
325 if isinstance(itm, BrowserDirectoryItem): |
330 if isinstance(itm, BrowserDirectoryItem): |
326 dirName = itm.dirName() |
331 dirName = itm.dirName() |
327 if ( |
332 if ( |
328 dirName != "" |
333 dirName != "" |
329 and not dirName.startswith("//") |
334 and not FileSystemUtilities.isRemoteFileName(dirName) |
330 and not dirName.startswith("\\\\") |
335 and not dirName.startswith(("//", "\\\\")) |
331 ): |
336 ): |
332 if dirName not in self.watcher.directories(): |
337 if dirName not in self.watcher.directories(): |
333 self.watcher.addPath(dirName) |
338 self.watcher.addPath(dirName) |
334 if dirName in self.watchedItems: |
339 if dirName in self.watchedItems: |
335 if itm not in self.watchedItems[dirName]: |
340 if itm not in self.watchedItems[dirName]: |
440 self.toplevelDirs.append( |
445 self.toplevelDirs.append( |
441 FileSystemUtilities.toNativeSeparators(d.absoluteFilePath()) |
446 FileSystemUtilities.toNativeSeparators(d.absoluteFilePath()) |
442 ) |
447 ) |
443 |
448 |
444 for d in self.toplevelDirs: |
449 for d in self.toplevelDirs: |
445 itm = BrowserDirectoryItem(self.rootItem, d) |
450 itm = BrowserDirectoryItem( |
|
451 self.rootItem, d, fsInterface=self.__remotefsInterface |
|
452 ) |
446 self._addItem(itm, self.rootItem) |
453 self._addItem(itm, self.rootItem) |
447 |
454 |
448 def interpreterChanged(self, interpreter): |
455 def interpreterChanged(self, interpreter): |
449 """ |
456 """ |
450 Public method to handle a change of the debug client's interpreter. |
457 Public method to handle a change of the debug client's interpreter. |
605 @param repopulate flag indicating a repopulation |
616 @param repopulate flag indicating a repopulation |
606 @type bool |
617 @type bool |
607 """ |
618 """ |
608 self._addWatchedItem(parentItem) |
619 self._addWatchedItem(parentItem) |
609 |
620 |
610 qdir = QDir(parentItem.dirName()) |
621 # TODO: add support for 'remote:' directories |
611 |
622 dirName = parentItem.dirName() |
612 dirFilter = ( |
623 if FileSystemUtilities.isPlainFileName(dirName): |
613 QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden |
624 qdir = QDir(dirName) |
614 ) |
625 |
615 entryInfoList = qdir.entryInfoList(dirFilter) |
626 dirFilter = ( |
616 if len(entryInfoList) > 0: |
627 QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden |
617 if repopulate: |
628 ) |
618 self.beginInsertRows( |
629 entryInfoList = qdir.entryInfoList(dirFilter) |
619 self.createIndex(parentItem.row(), 0, parentItem), |
630 if len(entryInfoList) > 0: |
620 0, |
631 if repopulate: |
621 len(entryInfoList) - 1, |
632 self.beginInsertRows( |
622 ) |
633 self.createIndex(parentItem.row(), 0, parentItem), |
623 for f in entryInfoList: |
634 0, |
624 if f.isDir(): |
635 len(entryInfoList) - 1, |
625 node = BrowserDirectoryItem( |
|
626 parentItem, |
|
627 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()), |
|
628 False, |
|
629 ) |
636 ) |
630 else: |
637 for f in entryInfoList: |
631 fileFilters = Preferences.getUI("BrowsersFileFilters").split(";") |
638 if f.isDir(): |
632 if fileFilters: |
639 node = BrowserDirectoryItem( |
633 fn = f.fileName() |
640 parentItem, |
634 if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters): |
641 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()), |
635 continue |
642 False, |
636 node = BrowserFileItem( |
643 ) |
637 parentItem, |
644 else: |
638 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()), |
645 fileFilters = Preferences.getUI("BrowsersFileFilters").split(";") |
|
646 if fileFilters: |
|
647 fn = f.fileName() |
|
648 if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters): |
|
649 continue |
|
650 node = BrowserFileItem( |
|
651 parentItem, |
|
652 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()), |
|
653 ) |
|
654 self._addItem(node, parentItem) |
|
655 if repopulate: |
|
656 self.endInsertRows() |
|
657 |
|
658 elif FileSystemUtilities.isRemoteFileName(dirName): |
|
659 entriesList = self.__remotefsInterface.listdir(dirName)[2] |
|
660 if len(entriesList) > 0: |
|
661 if repopulate: |
|
662 self.beginInsertRows( |
|
663 self.createIndex(parentItem.row(), 0, parentItem), |
|
664 0, |
|
665 len(entryInfoList) - 1, |
639 ) |
666 ) |
640 self._addItem(node, parentItem) |
667 for entry in entriesList: |
641 if repopulate: |
668 if entry["is_dir"]: |
642 self.endInsertRows() |
669 node = BrowserDirectoryItem( |
|
670 parentItem, |
|
671 FileSystemUtilities.remoteFileName(entry["path"]), |
|
672 False, |
|
673 fsInterface=self.__remotefsInterface, |
|
674 ) |
|
675 else: |
|
676 fileFilters = Preferences.getUI("BrowsersFileFilters").split(";") |
|
677 if fileFilters: |
|
678 fn = entry["name"] |
|
679 if any(fnmatch.fnmatch(fn, ff.strip()) for ff in fileFilters): |
|
680 continue |
|
681 node = BrowserFileItem( |
|
682 parentItem, |
|
683 FileSystemUtilities.remoteFileName(entry["path"]), |
|
684 fsInterface=self.__remotefsInterface, |
|
685 ) |
|
686 self._addItem(node, parentItem) |
|
687 if repopulate: |
|
688 self.endInsertRows() |
643 |
689 |
644 def populateSysPathItem(self, parentItem, repopulate=False): |
690 def populateSysPathItem(self, parentItem, repopulate=False): |
645 """ |
691 """ |
646 Public method to populate a sys.path item's subtree. |
692 Public method to populate a sys.path item's subtree. |
647 |
693 |
719 # special treatment done later |
766 # special treatment done later |
720 continue |
767 continue |
721 cl = dictionary[key] |
768 cl = dictionary[key] |
722 with contextlib.suppress(AttributeError): |
769 with contextlib.suppress(AttributeError): |
723 if cl.module == moduleName: |
770 if cl.module == moduleName: |
724 if isinstance(cl, ClbrBaseClasses.Class): |
771 if isinstance( |
|
772 cl, (ClbrBaseClasses.Class, ClbrBaseClasses.Module) |
|
773 ): |
725 node = BrowserClassItem(parentItem, cl, fileName) |
774 node = BrowserClassItem(parentItem, cl, fileName) |
726 elif isinstance(cl, ClbrBaseClasses.Function): |
775 elif isinstance(cl, ClbrBaseClasses.Function): |
727 node = BrowserMethodItem(parentItem, cl, fileName) |
776 node = BrowserMethodItem(parentItem, cl, fileName) |
728 self._addItem(node, parentItem) |
777 else: |
|
778 node = None |
|
779 if node: |
|
780 self._addItem(node, parentItem) |
729 if "@@Coding@@" in dictionary and Preferences.getUI("BrowserShowCoding"): |
781 if "@@Coding@@" in dictionary and Preferences.getUI("BrowserShowCoding"): |
730 node = BrowserCodingItem( |
782 node = BrowserCodingItem( |
731 parentItem, |
783 parentItem, |
732 QCoreApplication.translate("BrowserModel", "Coding: {0}").format( |
784 QCoreApplication.translate("BrowserModel", "Coding: {0}").format( |
733 dictionary["@@Coding@@"].coding |
785 dictionary["@@Coding@@"].coding |
1226 class BrowserDirectoryItem(BrowserItem): |
1280 class BrowserDirectoryItem(BrowserItem): |
1227 """ |
1281 """ |
1228 Class implementing the data structure for browser directory items. |
1282 Class implementing the data structure for browser directory items. |
1229 """ |
1283 """ |
1230 |
1284 |
1231 def __init__(self, parent, dinfo, full=True): |
1285 def __init__(self, parent, dinfo, full=True, fsInterface=None): |
1232 """ |
1286 """ |
1233 Constructor |
1287 Constructor |
1234 |
1288 |
1235 @param parent parent item |
1289 @param parent parent item |
1236 @type BrowserItem |
1290 @type BrowserItem |
1237 @param dinfo dinfo is the string for the directory |
1291 @param dinfo dinfo is the string for the directory |
1238 @type str |
1292 @type str |
1239 @param full flag indicating full pathname should be displayed |
1293 @param full flag indicating full pathname should be displayed (defaults to True) |
1240 @type bool |
1294 @type bool (optional) |
1241 """ |
1295 @param fsInterface reference to the 'eric-ide' server file system interface |
1242 self._dirName = os.path.abspath(dinfo) |
1296 (defaults to None) |
1243 dn = self._dirName if full else os.path.basename(self._dirName) |
1297 @type EricServerFileSystemInterface (optional) |
|
1298 """ |
|
1299 self.__fsInterface = fsInterface |
|
1300 |
|
1301 if FileSystemUtilities.isRemoteFileName(dinfo): |
|
1302 self._dirName = dinfo |
|
1303 dn = ( |
|
1304 self._dirName |
|
1305 if full |
|
1306 else self.__fsInterface.basename(self._dirName) |
|
1307 ) |
|
1308 else: |
|
1309 self._dirName = os.path.abspath(dinfo) |
|
1310 dn = self._dirName if full else os.path.basename(self._dirName) |
1244 BrowserItem.__init__(self, parent, dn) |
1311 BrowserItem.__init__(self, parent, dn) |
1245 |
1312 |
1246 self.type_ = BrowserItemType.Directory |
1313 self.type_ = BrowserItemType.Directory |
1247 if ( |
1314 if ( |
1248 not FileSystemUtilities.isDrive(self._dirName) |
1315 FileSystemUtilities.isPlainFileName(self._dirName) |
|
1316 and not FileSystemUtilities.isDrive(self._dirName) |
1249 and os.path.lexists(self._dirName) |
1317 and os.path.lexists(self._dirName) |
1250 and os.path.islink(self._dirName) |
1318 and os.path.islink(self._dirName) |
1251 ): |
1319 ): |
1252 self.symlink = True |
1320 self.symlink = True |
1253 self.icon = EricPixmapCache.getSymlinkIcon("dirClosed") |
1321 self.icon = EricPixmapCache.getSymlinkIcon("dirClosed") |
|
1322 elif FileSystemUtilities.isRemoteFileName(self._dirName): |
|
1323 self.icon = EricPixmapCache.getIcon("open-remote") |
1254 else: |
1324 else: |
1255 self.icon = EricPixmapCache.getIcon("dirClosed") |
1325 self.icon = EricPixmapCache.getIcon("dirClosed") |
1256 self._populated = False |
1326 self._populated = False |
1257 self._lazyPopulation = True |
1327 self._lazyPopulation = True |
1258 |
1328 |
1335 @rtype str |
1413 @rtype str |
1336 """ |
1414 """ |
1337 return "sys.path" |
1415 return "sys.path" |
1338 |
1416 |
1339 |
1417 |
|
1418 # TODO: add support for 'remote:' directories |
1340 class BrowserFileItem(BrowserItem): |
1419 class BrowserFileItem(BrowserItem): |
1341 """ |
1420 """ |
1342 Class implementing the data structure for browser file items. |
1421 Class implementing the data structure for browser file items. |
1343 """ |
1422 """ |
1344 |
1423 |
1345 def __init__(self, parent, finfo, full=True, sourceLanguage=""): |
1424 def __init__(self, parent, finfo, full=True, sourceLanguage="", fsInterface=None): |
1346 """ |
1425 """ |
1347 Constructor |
1426 Constructor |
1348 |
1427 |
1349 @param parent parent item |
1428 @param parent parent item |
1350 @type BrowserItem |
1429 @type BrowserItem |
1351 @param finfo the string for the file |
1430 @param finfo the string for the file |
1352 @type str |
1431 @type str |
1353 @param full flag indicating full pathname should be displayed |
1432 @param full flag indicating full pathname should be displayed (defaults to True) |
1354 @type bool |
1433 @type bool (optional) |
1355 @param sourceLanguage source code language of the project |
1434 @param sourceLanguage source code language of the project (defaults to "") |
1356 @type str |
1435 @type str (optional) |
1357 """ |
1436 @param fsInterface reference to the 'eric-ide' server file system interface |
1358 BrowserItem.__init__(self, parent, os.path.basename(finfo)) |
1437 (defaults to None) |
1359 |
1438 @type EricServerFileSystemInterface (optional) |
|
1439 """ |
|
1440 self.__fsInterface = fsInterface |
|
1441 |
|
1442 if FileSystemUtilities.isRemoteFileName(finfo): |
|
1443 dirname, basename = self.__fsInterface.split(finfo) |
|
1444 self.fileext = self.__fsInterface.splitext(finfo)[1].lower() |
|
1445 self._filename = finfo |
|
1446 else: |
|
1447 dirname, basename = os.path.split(finfo) |
|
1448 self.fileext = os.path.splitext(finfo)[1].lower() |
|
1449 self._filename = os.path.abspath(finfo) |
|
1450 |
|
1451 BrowserItem.__init__(self, parent, basename) |
|
1452 |
|
1453 self._dirName = dirname |
1360 self.type_ = BrowserItemType.File |
1454 self.type_ = BrowserItemType.File |
1361 self.fileext = os.path.splitext(finfo)[1].lower() |
|
1362 self._filename = os.path.abspath(finfo) |
|
1363 self._dirName = os.path.dirname(finfo) |
|
1364 self.sourceLanguage = sourceLanguage |
1455 self.sourceLanguage = sourceLanguage |
1365 |
1456 |
1366 self._moduleName = "" |
1457 self._moduleName = "" |
1367 |
1458 |
1368 pixName = "" |
1459 pixName = "" |
1369 if self.isPython3File(): |
1460 if self.isPython3File(): |
1370 pixName = "filePython" |
1461 pixName = "filePython" |
1371 self._populated = False |
1462 self._populated = False |
1372 self._lazyPopulation = True |
1463 self._lazyPopulation = True |
1373 self._moduleName = os.path.basename(finfo) |
1464 self._moduleName = basename |
1374 elif self.isCythonFile(): |
1465 elif self.isCythonFile(): |
1375 pixName = "lexerCython" |
1466 pixName = "lexerCython" |
1376 self._populated = False |
1467 self._populated = False |
1377 self._lazyPopulation = True |
1468 self._lazyPopulation = True |
1378 self._moduleName = os.path.basename(finfo) |
1469 self._moduleName = basename |
1379 elif self.isRubyFile(): |
1470 elif self.isRubyFile(): |
1380 pixName = "fileRuby" |
1471 pixName = "fileRuby" |
1381 self._populated = False |
1472 self._populated = False |
1382 self._lazyPopulation = True |
1473 self._lazyPopulation = True |
1383 self._moduleName = os.path.basename(finfo) |
1474 self._moduleName = basename |
1384 elif self.isDesignerFile(): |
1475 elif self.isDesignerFile(): |
1385 pixName = "fileDesigner" |
1476 pixName = "fileDesigner" |
1386 elif self.isLinguistFile(): |
1477 elif self.isLinguistFile(): |
1387 if self.fileext == ".ts": |
1478 if self.fileext == ".ts": |
1388 pixName = "fileLinguist" |
1479 pixName = "fileLinguist" |
1402 pixName = "fileD" |
1493 pixName = "fileD" |
1403 elif self.isJavaScriptFile(): |
1494 elif self.isJavaScriptFile(): |
1404 pixName = "fileJavascript" |
1495 pixName = "fileJavascript" |
1405 self._populated = False |
1496 self._populated = False |
1406 self._lazyPopulation = True |
1497 self._lazyPopulation = True |
1407 self._moduleName = os.path.basename(finfo) |
1498 self._moduleName = basename |
1408 elif self.isEricGraphicsFile(): |
1499 elif self.isEricGraphicsFile(): |
1409 pixName = "fileUML" |
1500 pixName = "fileUML" |
1410 elif self.isParsableFile(): |
1501 elif self.isParsableFile(): |
1411 pixName = ClassBrowsers.getIcon(self._filename) |
1502 pixName = ClassBrowsers.getIcon(self._filename) |
1412 self._populated = False |
1503 self._populated = False |
1413 self._lazyPopulation = True |
1504 self._lazyPopulation = True |
1414 self._moduleName = os.path.basename(finfo) |
1505 self._moduleName = basename |
1415 else: |
1506 else: |
1416 pixName = "fileMisc" |
1507 pixName = "fileMisc" |
1417 |
1508 |
1418 if os.path.lexists(self._filename) and os.path.islink(self._filename): |
1509 if ( |
|
1510 FileSystemUtilities.isPlainFileName(self._filename) |
|
1511 and os.path.lexists(self._filename) |
|
1512 and os.path.islink(self._filename) |
|
1513 ): |
1419 self.symlink = True |
1514 self.symlink = True |
1420 self.icon = EricPixmapCache.getSymlinkIcon(pixName) |
1515 self.icon = EricPixmapCache.getSymlinkIcon(pixName) |
1421 else: |
1516 else: |
1422 self.icon = EricPixmapCache.getIcon(pixName) |
1517 self.icon = EricPixmapCache.getIcon(pixName) |
1423 |
1518 |
1428 @param finfo the string for the file |
1523 @param finfo the string for the file |
1429 @type str |
1524 @type str |
1430 @param full flag indicating full pathname should be displayed |
1525 @param full flag indicating full pathname should be displayed |
1431 @type bool |
1526 @type bool |
1432 """ |
1527 """ |
1433 self._filename = os.path.abspath(finfo) |
1528 if FileSystemUtilities.isRemoteFileName(finfo): |
1434 self.itemData[0] = os.path.basename(finfo) |
1529 dirname, basename = self.__fsInterface.split(finfo) |
1435 self.fileext = os.path.splitext(finfo)[1].lower() |
1530 self.fileext = self.__fsInterface.splitext(finfo)[1].lower() |
1436 if self.isPython3File() or self.isRubyFile() or self.isParsableFile(): |
1531 self._filename = finfo |
1437 self._dirName = os.path.dirname(finfo) |
1532 else: |
1438 self._moduleName = os.path.basename(finfo) |
1533 dirname, basename = os.path.split(finfo) |
|
1534 self.fileext = os.path.splitext(finfo)[1].lower() |
|
1535 self._filename = os.path.abspath(finfo) |
|
1536 |
|
1537 self.itemData[0] = basename |
|
1538 if ( |
|
1539 self.isPython3File() |
|
1540 or self.isCythonFile() |
|
1541 or self.isRubyFile() |
|
1542 or self.isJavaScriptFile() |
|
1543 or self.isParsableFile() |
|
1544 ): |
|
1545 self._dirName = dirname |
|
1546 self._moduleName = basename |
1439 |
1547 |
1440 def fileName(self): |
1548 def fileName(self): |
1441 """ |
1549 """ |
1442 Public method returning the filename. |
1550 Public method returning the filename. |
1443 |
1551 |
1647 "BrowsersListFoldersFirst" |
1755 "BrowsersListFoldersFirst" |
1648 ): |
1756 ): |
1649 return order == Qt.SortOrder.DescendingOrder |
1757 return order == Qt.SortOrder.DescendingOrder |
1650 |
1758 |
1651 if issubclass(other.__class__, BrowserFileItem): |
1759 if issubclass(other.__class__, BrowserFileItem): |
1652 sinit = os.path.basename(self._filename).startswith("__init__.py") |
1760 if FileSystemUtilities.isRemoteFileName(self._filename): |
1653 oinit = os.path.basename(other.fileName()).startswith("__init__.py") |
1761 basename = self.__fsInterface.basename(self._filename) |
|
1762 else: |
|
1763 basename = os.path.basename(self._filename) |
|
1764 sinit = basename.startswith("__init__.py") |
|
1765 |
|
1766 if FileSystemUtilities.isRemoteFileName(other.fileName()): |
|
1767 basename = self.__fsInterface.basename(other.fileName()) |
|
1768 else: |
|
1769 basename = os.path.basename(other.fileName()) |
|
1770 oinit = basename.startswith("__init__.py") |
|
1771 |
1654 if sinit and not oinit: |
1772 if sinit and not oinit: |
1655 return order == Qt.SortOrder.AscendingOrder |
1773 return order == Qt.SortOrder.AscendingOrder |
1656 if not sinit and oinit: |
1774 if not sinit and oinit: |
1657 return order == Qt.SortOrder.DescendingOrder |
1775 return order == Qt.SortOrder.DescendingOrder |
1658 |
1776 |