src/eric7/Project/Project.py

branch
eric7-maintenance
changeset 10814
ba20efe10336
parent 10694
f46c1e224e8a
parent 10806
2f6df822e3b9
child 10873
4e8e63df7893
equal deleted inserted replaced
10734:2b015db9761a 10814:ba20efe10336
29 pyqtSlot, 29 pyqtSlot,
30 ) 30 )
31 from PyQt6.QtGui import QAction, QKeySequence 31 from PyQt6.QtGui import QAction, QKeySequence
32 from PyQt6.QtWidgets import QDialog, QInputDialog, QLineEdit, QMenu, QToolBar 32 from PyQt6.QtWidgets import QDialog, QInputDialog, QLineEdit, QMenu, QToolBar
33 33
34 from eric7 import Globals, Preferences, Utilities 34 from eric7 import EricUtilities, Preferences, Utilities
35 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction 35 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction
36 from eric7.CodeFormatting.BlackUtilities import aboutBlack 36 from eric7.CodeFormatting.BlackUtilities import aboutBlack
37 from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction 37 from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction
38 from eric7.CodeFormatting.IsortUtilities import aboutIsort 38 from eric7.CodeFormatting.IsortUtilities import aboutIsort
39 from eric7.EricGui import EricPixmapCache 39 from eric7.EricGui import EricPixmapCache
42 from eric7.EricWidgets import EricFileDialog, EricMessageBox 42 from eric7.EricWidgets import EricFileDialog, EricMessageBox
43 from eric7.EricWidgets.EricApplication import ericApp 43 from eric7.EricWidgets.EricApplication import ericApp
44 from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog 44 from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog
45 from eric7.EricWidgets.EricProgressDialog import EricProgressDialog 45 from eric7.EricWidgets.EricProgressDialog import EricProgressDialog
46 from eric7.Globals import recentNameProject 46 from eric7.Globals import recentNameProject
47 from eric7.RemoteServerInterface import EricServerFileDialog
47 from eric7.Sessions.SessionFile import SessionFile 48 from eric7.Sessions.SessionFile import SessionFile
48 from eric7.SystemUtilities import ( 49 from eric7.SystemUtilities import (
49 FileSystemUtilities, 50 FileSystemUtilities,
50 OSUtilities, 51 OSUtilities,
51 PythonUtilities, 52 PythonUtilities,
156 eols = [os.linesep, "\n", "\r", "\r\n"] 157 eols = [os.linesep, "\n", "\r", "\r\n"]
157 158
158 DefaultMake = "make" 159 DefaultMake = "make"
159 DefaultMakefile = "makefile" 160 DefaultMakefile = "makefile"
160 161
161 def __init__(self, parent=None, filename=None): 162 def __init__(self, parent=None, filename=None, remoteServer=None):
162 """ 163 """
163 Constructor 164 Constructor
164 165
165 @param parent parent widget (usually the ui object) 166 @param parent parent widget (usually the ui object)
166 @type QWidget 167 @type QWidget
167 @param filename optional filename of a project file to open 168 @param filename optional filename of a project file to open (defaults to None)
168 @type str 169 @type str (optional)
170 @param remoteServer reference to the 'eric-ide' server interface object
171 @type EricServerInterface
169 """ 172 """
170 super().__init__(parent) 173 super().__init__(parent)
171 174
172 self.ui = parent 175 self.ui = parent
176 self.__remoteServer = remoteServer
177 self.__remotefsInterface = remoteServer.getServiceInterface("FileSystem")
173 178
174 self.__progLanguages = [ 179 self.__progLanguages = [
175 "Python3", 180 "Python3",
176 "MicroPython", 181 "MicroPython",
177 "Ruby", 182 "Ruby",
208 if filename is not None: 213 if filename is not None:
209 self.openProject(filename) 214 self.openProject(filename)
210 else: 215 else:
211 self.vcs = self.initVCS() 216 self.vcs = self.initVCS()
212 217
213 self.__model = ProjectBrowserModel(self) 218 self.__model = ProjectBrowserModel(self, fsInterface=self.__remotefsInterface)
214 219
215 self.codemetrics = None 220 self.codemetrics = None
216 self.codecoverage = None 221 self.codecoverage = None
217 self.profiledata = None 222 self.profiledata = None
218 self.applicationDiagram = None 223 self.applicationDiagram = None
506 self.translationsRoot = "" # the translations prefix 511 self.translationsRoot = "" # the translations prefix
507 self.name = "" 512 self.name = ""
508 self.opened = False 513 self.opened = False
509 self.subdirs = [] 514 self.subdirs = []
510 # record the project dir as a relative path (i.e. empty path) 515 # record the project dir as a relative path (i.e. empty path)
511 self.otherssubdirs = []
512 self.vcs = None 516 self.vcs = None
513 self.vcsRequested = False 517 self.vcsRequested = False
514 self.dbgVirtualEnv = "" 518 self.dbgVirtualEnv = ""
515 self.dbgCmdline = "" 519 self.dbgCmdline = ""
516 self.dbgWd = "" 520 self.dbgWd = ""
974 self.recent = [] 978 self.recent = []
975 Preferences.Prefs.rsettings.sync() 979 Preferences.Prefs.rsettings.sync()
976 rp = Preferences.Prefs.rsettings.value(recentNameProject) 980 rp = Preferences.Prefs.rsettings.value(recentNameProject)
977 if rp is not None: 981 if rp is not None:
978 for f in rp: 982 for f in rp:
979 if pathlib.Path(f).exists(): 983 if (
984 FileSystemUtilities.isRemoteFileName(f)
985 and (
986 not self.__remoteServer.isServerConnected()
987 or self.__remotefsInterface.exists(f)
988 )
989 ) or pathlib.Path(f).exists():
980 self.recent.append(f) 990 self.recent.append(f)
981 991
982 def __saveRecent(self): 992 def __saveRecent(self):
983 """ 993 """
984 Private method to save the list of recently opened filenames. 994 Private method to save the list of recently opened filenames.
1096 @param index key of the list to be checked 1106 @param index key of the list to be checked
1097 @type str 1107 @type str
1098 """ 1108 """
1099 removed = False 1109 removed = False
1100 removelist = [] 1110 removelist = []
1101 for file in self.__pdata[index]: 1111 if FileSystemUtilities.isRemoteFileName(self.ppath):
1102 if not os.path.exists(os.path.join(self.ppath, file)): 1112 for file in self.__pdata[index]:
1103 removelist.append(file) 1113 if not self.__remotefsInterface.exists(
1104 removed = True 1114 self.__remotefsInterface.join(self.ppath, file)
1115 ):
1116 removelist.append(file)
1117 removed = True
1118 else:
1119 for file in self.__pdata[index]:
1120 if not os.path.exists(os.path.join(self.ppath, file)):
1121 removelist.append(file)
1122 removed = True
1105 1123
1106 if removed: 1124 if removed:
1107 for file in removelist: 1125 for file in removelist:
1108 self.__pdata[index].remove(file) 1126 self.__pdata[index].remove(file)
1109 self.setDirty(True) 1127 self.setDirty(True)
1110 1128
1111 def __readProject(self, fn): 1129 def __readProject(self, fn):
1112 """ 1130 """
1113 Private method to read in a project (.epj) file. 1131 Private method to read in a project file (.epj).
1114 1132
1115 @param fn filename of the project file to be read 1133 @param fn filename of the project file to be read
1116 @type str 1134 @type str
1117 @return flag indicating success 1135 @return flag indicating success
1118 @rtype bool 1136 @rtype bool
1119 """ 1137 """
1120 with EricOverrideCursor(): 1138 with EricOverrideCursor():
1121 res = self.__projectFile.readFile(fn) 1139 res = self.__projectFile.readFile(fn)
1122 1140
1123 if res: 1141 if res:
1124 self.pfile = os.path.abspath(fn) 1142 if FileSystemUtilities.isRemoteFileName(fn):
1125 self.ppath = os.path.abspath(os.path.dirname(fn)) 1143 self.pfile = fn
1144 self.ppath = self.__remotefsInterface.dirname(fn)
1145 self.name = self.__remotefsInterface.splitext(
1146 self.__remotefsInterface.basename(fn)
1147 )[0]
1148 self.__remotefsInterface.populateFsCache(self.ppath)
1149 else:
1150 self.pfile = os.path.abspath(fn)
1151 self.ppath = os.path.abspath(os.path.dirname(fn))
1152 self.name = os.path.splitext(os.path.basename(fn))[0]
1126 1153
1127 # insert filename into list of recently opened projects 1154 # insert filename into list of recently opened projects
1128 self.__syncRecent() 1155 self.__syncRecent()
1129 1156
1130 if self.__pdata["TRANSLATIONPATTERN"]: 1157 if self.__pdata["TRANSLATIONPATTERN"]:
1131 self.translationsRoot = self.__pdata["TRANSLATIONPATTERN"].split( 1158 self.translationsRoot = self.__pdata["TRANSLATIONPATTERN"].split(
1132 "%language%" 1159 "%language%"
1133 )[0] 1160 )[0]
1134 elif self.__pdata["MAINSCRIPT"]: 1161 elif self.__pdata["MAINSCRIPT"]:
1135 self.translationsRoot = os.path.splitext(self.__pdata["MAINSCRIPT"])[0] 1162 self.translationsRoot = os.path.splitext(self.__pdata["MAINSCRIPT"])[0]
1136 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): 1163
1137 dn = self.translationsRoot 1164 if FileSystemUtilities.isRemoteFileName(self.ppath):
1165 if self.__remotefsInterface.isdir(
1166 self.__remotefsInterface.join(self.ppath, self.translationsRoot)
1167 ):
1168 dn = self.translationsRoot
1169 else:
1170 dn = self.__remotefsInterface.dirname(self.translationsRoot)
1138 else: 1171 else:
1139 dn = os.path.dirname(self.translationsRoot) 1172 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)):
1140 if dn not in self.subdirs: 1173 dn = self.translationsRoot
1174 else:
1175 dn = os.path.dirname(self.translationsRoot)
1176 if dn and dn not in self.subdirs:
1141 self.subdirs.append(dn) 1177 self.subdirs.append(dn)
1142
1143 self.name = os.path.splitext(os.path.basename(fn))[0]
1144 1178
1145 # check, if the files of the project still exist in the 1179 # check, if the files of the project still exist in the
1146 # project directory 1180 # project directory
1147 for fileCategory in self.getFileCategories(): 1181 for fileCategory in self.getFileCategories():
1148 self.__checkFilesExist(fileCategory) 1182 self.__checkFilesExist(fileCategory)
1149 1183
1150 # get the names of subdirectories the files are stored in 1184 # get the names of subdirectories the files are stored in
1151 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: 1185 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]:
1152 for fn in self.__pdata[fileCategory]: 1186 for fn in self.__pdata[fileCategory]:
1153 dn = os.path.dirname(fn) 1187 dn = (
1154 if dn not in self.subdirs: 1188 self.__remotefsInterface.dirname(fn)
1189 if FileSystemUtilities.isRemoteFileName(self.ppath)
1190 else os.path.dirname(fn)
1191 )
1192 if dn and dn not in self.subdirs:
1155 self.subdirs.append(dn) 1193 self.subdirs.append(dn)
1156 1194
1157 # get the names of other subdirectories 1195 # get the names of other subdirectories
1158 for fn in self.__pdata["OTHERS"]: 1196 for fn in self.__pdata["OTHERS"]:
1159 dn = os.path.dirname(fn) 1197 dn = (
1160 if dn not in self.otherssubdirs: 1198 self.__remotefsInterface.dirname(fn)
1161 self.otherssubdirs.append(dn) 1199 if FileSystemUtilities.isRemoteFileName(fn)
1200 else os.path.dirname(fn)
1201 )
1162 1202
1163 return res 1203 return res
1164 1204
1165 def __writeProject(self, fn=None): 1205 def __writeProject(self, fn=None):
1166 """ 1206 """
1194 1234
1195 with EricOverrideCursor(): 1235 with EricOverrideCursor():
1196 res = self.__projectFile.writeFile(fn) 1236 res = self.__projectFile.writeFile(fn)
1197 1237
1198 if res: 1238 if res:
1199 self.pfile = os.path.abspath(fn) 1239 if FileSystemUtilities.isRemoteFileName(fn):
1200 self.ppath = os.path.abspath(os.path.dirname(fn)) 1240 self.pfile = fn
1201 self.name = os.path.splitext(os.path.basename(fn))[0] 1241 self.ppath = self.__remotefsInterface.dirname(fn)
1242 self.name = self.__remotefsInterface.splitext(
1243 self.__remotefsInterface.basename(fn)
1244 )[0]
1245 else:
1246 self.pfile = os.path.abspath(fn)
1247 self.ppath = os.path.abspath(os.path.dirname(fn))
1248 self.name = os.path.splitext(os.path.basename(fn))[0]
1202 self.setDirty(False) 1249 self.setDirty(False)
1203 1250
1204 # insert filename into list of recently opened projects 1251 # insert filename into list of recently opened projects
1205 self.__syncRecent() 1252 self.__syncRecent()
1206 1253
1211 Private method to read in the user specific project file (.eqj). 1258 Private method to read in the user specific project file (.eqj).
1212 """ 1259 """
1213 if self.pfile is None: 1260 if self.pfile is None:
1214 return 1261 return
1215 1262
1216 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) 1263 if FileSystemUtilities.isRemoteFileName(self.pfile):
1217 fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn1)) 1264 fn1, _ext = self.__remotefsInterface.splitext(
1218 if os.path.exists(fn): 1265 self.__remotefsInterface.basename(self.pfile)
1219 self.__userProjectFile.readFile(fn) 1266 )
1267 fn = self.__remotefsInterface.join(
1268 self.getProjectManagementDir(), f"{fn1}.eqj"
1269 )
1270 if not self.__remotefsInterface.exists(fn):
1271 return
1272 else:
1273 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1274 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.eqj")
1275 if not os.path.exists(fn):
1276 return
1277
1278 self.__userProjectFile.readFile(fn)
1220 1279
1221 def __writeUserProperties(self): 1280 def __writeUserProperties(self):
1222 """ 1281 """
1223 Private method to write the user specific project data to a JSON file. 1282 Private method to write the user specific project data to a JSON file.
1224 """ 1283 """
1225 if self.pfile is None: 1284 if self.pfile is None:
1226 return 1285 return
1227 1286
1228 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1287 if FileSystemUtilities.isRemoteFileName(self.pfile):
1229 fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn)) 1288 fn1, _ext = self.__remotefsInterface.splitext(
1289 self.__remotefsInterface.basename(self.pfile)
1290 )
1291 fn = self.__remotefsInterface.join(
1292 self.getProjectManagementDir(), f"{fn1}.eqj"
1293 )
1294 else:
1295 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1296 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.eqj")
1230 1297
1231 with EricOverrideCursor(): 1298 with EricOverrideCursor():
1232 self.__userProjectFile.writeFile(fn) 1299 self.__userProjectFile.writeFile(fn)
1233 1300
1234 def __showContextMenuSession(self): 1301 def __showContextMenuSession(self):
1237 """ 1304 """
1238 enable = True 1305 enable = True
1239 if self.pfile is None: 1306 if self.pfile is None:
1240 enable = False 1307 enable = False
1241 else: 1308 else:
1242 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1309 if FileSystemUtilities.isRemoteFileName(self.pfile):
1243 fn_sess = os.path.join(self.getProjectManagementDir(), "{0}.esj".format(fn)) 1310 fn, _ext = self.__remotefsInterface.splitext(
1244 enable = os.path.exists(fn_sess) 1311 self.__remotefsInterface.basename(self.pfile)
1312 )
1313 fn_sess = self.__remotefsInterface.join(
1314 self.getProjectManagementDir(), f"{fn}.esj"
1315 )
1316 enable = self.__remotefsInterface.exists(fn)
1317 else:
1318 fn, _ext = os.path.splitext(os.path.basename(self.pfile))
1319 fn_sess = os.path.join(self.getProjectManagementDir(), f"{fn}.esj")
1320 enable = os.path.exists(fn_sess)
1245 self.sessActGrp.findChild(QAction, "project_load_session").setEnabled(enable) 1321 self.sessActGrp.findChild(QAction, "project_load_session").setEnabled(enable)
1246 self.sessActGrp.findChild(QAction, "project_delete_session").setEnabled(enable) 1322 self.sessActGrp.findChild(QAction, "project_delete_session").setEnabled(enable)
1247 1323
1248 @pyqtSlot() 1324 @pyqtSlot()
1249 def __readSession(self, quiet=False, indicator=""): 1325 def __readSession(self, quiet=False, indicator=""):
1263 self.tr("Read Project Session"), 1339 self.tr("Read Project Session"),
1264 self.tr("Please save the project first."), 1340 self.tr("Please save the project first."),
1265 ) 1341 )
1266 return 1342 return
1267 1343
1268 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) 1344 if FileSystemUtilities.isRemoteFileName(self.pfile):
1269 fn = os.path.join( 1345 fn1, _ext = self.__remotefsInterface.splitext(
1270 self.getProjectManagementDir(), "{0}{1}.esj".format(fn1, indicator) 1346 self.__remotefsInterface.basename(self.pfile)
1271 ) 1347 )
1272 if os.path.exists(fn): 1348 fn = self.__remotefsInterface.join(
1273 self.__sessionFile.readFile(fn) 1349 self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
1350 )
1351 if not self.__remotefsInterface.exists(fn):
1352 return
1353 else:
1354 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1355 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}{indicator}.esj")
1356 if not os.path.exists(fn):
1357 return
1358
1359 self.__sessionFile.readFile(fn)
1274 1360
1275 @pyqtSlot() 1361 @pyqtSlot()
1276 def __writeSession(self, quiet=False, indicator=""): 1362 def __writeSession(self, quiet=False, indicator=""):
1277 """ 1363 """
1278 Private method to write the session data to an XML file (.esj). 1364 Private method to write the session data to an XML file (.esj).
1290 self.tr("Save Project Session"), 1376 self.tr("Save Project Session"),
1291 self.tr("Please save the project first."), 1377 self.tr("Please save the project first."),
1292 ) 1378 )
1293 return 1379 return
1294 1380
1295 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1381 if FileSystemUtilities.isRemoteFileName(self.pfile):
1296 fn = os.path.join( 1382 fn1, _ext = self.__remotefsInterface.splitext(
1297 self.getProjectManagementDir(), "{0}{1}.esj".format(fn, indicator) 1383 self.__remotefsInterface.basename(self.pfile)
1298 ) 1384 )
1385 fn = self.__remotefsInterface.join(
1386 self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
1387 )
1388 else:
1389 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1390 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}{indicator}.esj")
1299 1391
1300 self.__sessionFile.writeFile(fn) 1392 self.__sessionFile.writeFile(fn)
1301 1393
1302 def __deleteSession(self): 1394 def __deleteSession(self):
1303 """ 1395 """
1309 self.tr("Delete Project Session"), 1401 self.tr("Delete Project Session"),
1310 self.tr("Please save the project first."), 1402 self.tr("Please save the project first."),
1311 ) 1403 )
1312 return 1404 return
1313 1405
1314 fname, ext = os.path.splitext(os.path.basename(self.pfile)) 1406 try:
1315 1407 if FileSystemUtilities.isRemoteFileName(self.pfile):
1316 fn = os.path.join(self.getProjectManagementDir(), f"{fname}.esj") 1408 title = self.tr("Delete Remote Project Session")
1317 if os.path.exists(fn): 1409 fname, _ext = self.__remotefsInterface.splitext(
1318 try: 1410 self.__remotefsInterface.basename(self.pfile)
1319 os.remove(fn)
1320 except OSError:
1321 EricMessageBox.critical(
1322 self.ui,
1323 self.tr("Delete Project Session"),
1324 self.tr(
1325 "<p>The project session file <b>{0}</b> could"
1326 " not be deleted.</p>"
1327 ).format(fn),
1328 ) 1411 )
1412 fn = self.__remotefsInterface.join(
1413 self.getProjectManagementDir(), f"{fname}.esj"
1414 )
1415 if self.__remotefsInterface.exists(fn):
1416 self.__remotefsInterface.remove(fn)
1417 else:
1418 title = self.tr("Delete Project Session")
1419 fname, _ext = os.path.splitext(os.path.basename(self.pfile))
1420 fn = os.path.join(self.getProjectManagementDir(), f"{fname}.esj")
1421 if os.path.exists(fn):
1422 os.remove(fn)
1423 except OSError:
1424 EricMessageBox.critical(
1425 self.ui,
1426 title,
1427 self.tr(
1428 "<p>The project session file <b>{0}</b> could"
1429 " not be deleted.</p>"
1430 ).format(fn),
1431 )
1329 1432
1330 def __readTasks(self): 1433 def __readTasks(self):
1331 """ 1434 """
1332 Private method to read in the project tasks file (.etj). 1435 Private method to read in the project tasks file (.etj).
1333 """ 1436 """
1337 self.tr("Read Tasks"), 1440 self.tr("Read Tasks"),
1338 self.tr("Please save the project first."), 1441 self.tr("Please save the project first."),
1339 ) 1442 )
1340 return 1443 return
1341 1444
1342 base, ext = os.path.splitext(os.path.basename(self.pfile)) 1445 if FileSystemUtilities.isRemoteFileName(self.pfile):
1343 fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(base)) 1446 base, _ext = self.__remotefsInterface.splitext(
1344 if os.path.exists(fn): 1447 self.__remotefsInterface.basename(self.pfile)
1345 self.__tasksFile.readFile(fn) 1448 )
1449 fn = self.__remotefsInterface.join(
1450 self.getProjectManagementDir(), f"{base}.etj"
1451 )
1452 if not self.__remotefsInterface.exists(fn):
1453 return
1454 else:
1455 base, ext = os.path.splitext(os.path.basename(self.pfile))
1456 fn = os.path.join(self.getProjectManagementDir(), f"{base}.etj")
1457 if not os.path.exists(fn):
1458 return
1459
1460 self.__tasksFile.readFile(fn)
1346 1461
1347 def writeTasks(self): 1462 def writeTasks(self):
1348 """ 1463 """
1349 Public method to write the tasks data to a JSON file (.etj). 1464 Public method to write the tasks data to a JSON file (.etj).
1350 """ 1465 """
1351 if self.pfile is None: 1466 if self.pfile is None:
1352 return 1467 return
1353 1468
1354 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1469 if FileSystemUtilities.isRemoteFileName(self.pfile):
1355 1470 base, _ext = self.__remotefsInterface.splitext(
1356 fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(fn)) 1471 self.__remotefsInterface.basename(self.pfile)
1472 )
1473 fn = self.__remotefsInterface.join(
1474 self.getProjectManagementDir(), f"{base}.etj"
1475 )
1476 else:
1477 base, ext = os.path.splitext(os.path.basename(self.pfile))
1478 fn = os.path.join(self.getProjectManagementDir(), f"{base}.etj")
1479
1357 self.__tasksFile.writeFile(fn) 1480 self.__tasksFile.writeFile(fn)
1358 1481
1359 def __showContextMenuDebugger(self): 1482 def __showContextMenuDebugger(self):
1360 """ 1483 """
1361 Private slot called before the Debugger menu is shown. 1484 Private slot called before the Debugger menu is shown.
1362 """ 1485 """
1363 enable = True 1486 enable = True
1364 if self.pfile is None: 1487 if self.pfile is None:
1365 enable = False 1488 enable = False
1366 else: 1489 else:
1367 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1490 if FileSystemUtilities.isRemoteFileName(self.pfile):
1368 fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn)) 1491 fn1, _ext = self.__remotefsInterface.splitext(
1369 enable = os.path.exists(fn) 1492 self.__remotefsInterface.basename(self.pfile)
1493 )
1494 fn = self.__remotefsInterface.join(
1495 self.getProjectManagementDir(), f"{fn1}.edj"
1496 )
1497 enable = self.__remotefsInterface.exists(fn)
1498 else:
1499 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1500 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
1501 enable = os.path.exists(fn)
1370 self.dbgActGrp.findChild( 1502 self.dbgActGrp.findChild(
1371 QAction, "project_debugger_properties_load" 1503 QAction, "project_debugger_properties_load"
1372 ).setEnabled(enable) 1504 ).setEnabled(enable)
1373 self.dbgActGrp.findChild( 1505 self.dbgActGrp.findChild(
1374 QAction, "project_debugger_properties_delete" 1506 QAction, "project_debugger_properties_delete"
1390 self.tr("Read Debugger Properties"), 1522 self.tr("Read Debugger Properties"),
1391 self.tr("Please save the project first."), 1523 self.tr("Please save the project first."),
1392 ) 1524 )
1393 return 1525 return
1394 1526
1395 fn1, ext = os.path.splitext(os.path.basename(self.pfile)) 1527 if FileSystemUtilities.isRemoteFileName(self.pfile):
1396 fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn1)) 1528 fn1, _ext = self.__remotefsInterface.splitext(
1397 if os.path.exists(fn) and self.__debuggerPropertiesFile.readFile(fn): 1529 self.__remotefsInterface.basename(self.pfile)
1530 )
1531 fn = self.__remotefsInterface.join(
1532 self.getProjectManagementDir(), f"{fn1}.edj"
1533 )
1534 if not self.__remotefsInterface.exists(fn):
1535 return
1536 else:
1537 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1538 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
1539 if not os.path.exists(fn):
1540 return
1541 if self.__debuggerPropertiesFile.readFile(fn):
1398 self.debugPropertiesLoaded = True 1542 self.debugPropertiesLoaded = True
1399 self.debugPropertiesChanged = False 1543 self.debugPropertiesChanged = False
1400 1544
1401 @pyqtSlot() 1545 @pyqtSlot()
1402 def __writeDebugProperties(self, quiet=False): 1546 def __writeDebugProperties(self, quiet=False):
1414 self.tr("Save Debugger Properties"), 1558 self.tr("Save Debugger Properties"),
1415 self.tr("Please save the project first."), 1559 self.tr("Please save the project first."),
1416 ) 1560 )
1417 return 1561 return
1418 1562
1419 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 1563 if FileSystemUtilities.isRemoteFileName(self.pfile):
1420 fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn)) 1564 fn1, _ext = self.__remotefsInterface.splitext(
1565 self.__remotefsInterface.basename(self.pfile)
1566 )
1567 fn = self.__remotefsInterface.join(
1568 self.getProjectManagementDir(), f"{fn1}.edj"
1569 )
1570 else:
1571 fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
1572 fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
1421 1573
1422 with EricOverrideCursor(): 1574 with EricOverrideCursor():
1423 self.__debuggerPropertiesFile.writeFile(fn) 1575 self.__debuggerPropertiesFile.writeFile(fn)
1424 1576
1425 def __deleteDebugProperties(self): 1577 def __deleteDebugProperties(self):
1432 self.tr("Delete Debugger Properties"), 1584 self.tr("Delete Debugger Properties"),
1433 self.tr("Please save the project first."), 1585 self.tr("Please save the project first."),
1434 ) 1586 )
1435 return 1587 return
1436 1588
1437 fname, ext = os.path.splitext(os.path.basename(self.pfile)) 1589 try:
1438 1590 if FileSystemUtilities.isRemoteFileName(self.pfile):
1439 fn = os.path.join(self.getProjectManagementDir(), f"{fname}.edj") 1591 title = self.tr("Delete Remote Debugger Properties")
1440 if os.path.exists(fn): 1592 fname, _ext = self.__remotefsInterface.splitext(
1441 try: 1593 self.__remotefsInterface.basename(self.pfile)
1442 os.remove(fn)
1443 except OSError:
1444 EricMessageBox.critical(
1445 self.ui,
1446 self.tr("Delete Debugger Properties"),
1447 self.tr(
1448 "<p>The project debugger properties file"
1449 " <b>{0}</b> could not be deleted.</p>"
1450 ).format(fn),
1451 ) 1594 )
1595 fn = self.__remotefsInterface.join(
1596 self.getProjectManagementDir(), f"{fname}.edj"
1597 )
1598 if self.__remotefsInterface.exists(fn):
1599 self.__remotefsInterface.remove(fn)
1600 else:
1601 title = self.tr("Delete Debugger Properties")
1602 fname, _ext = os.path.splitext(os.path.basename(self.pfile))
1603 fn = os.path.join(self.getProjectManagementDir(), f"{fname}.edj")
1604 if os.path.exists(fn):
1605 os.remove(fn)
1606 except OSError:
1607 EricMessageBox.critical(
1608 self.ui,
1609 title,
1610 self.tr(
1611 "<p>The project debugger properties file"
1612 " <b>{0}</b> could not be deleted.</p>"
1613 ).format(fn),
1614 )
1452 1615
1453 def __initDebugProperties(self): 1616 def __initDebugProperties(self):
1454 """ 1617 """
1455 Private method to initialize the debug properties. 1618 Private method to initialize the debug properties.
1456 """ 1619 """
1487 """ 1650 """
1488 Private slot to display the debugger properties dialog. 1651 Private slot to display the debugger properties dialog.
1489 """ 1652 """
1490 from .DebuggerPropertiesDialog import DebuggerPropertiesDialog 1653 from .DebuggerPropertiesDialog import DebuggerPropertiesDialog
1491 1654
1492 dlg = DebuggerPropertiesDialog(self) 1655 dlg = DebuggerPropertiesDialog(
1656 self, isRemote=FileSystemUtilities.isRemoteFileName(self.ppath)
1657 )
1493 if dlg.exec() == QDialog.DialogCode.Accepted: 1658 if dlg.exec() == QDialog.DialogCode.Accepted:
1494 dlg.storeData() 1659 dlg.storeData()
1495 1660
1496 def getDebugProperty(self, key): 1661 def getDebugProperty(self, key):
1497 """ 1662 """
1500 @param key key of the property 1665 @param key key of the property
1501 @type str 1666 @type str
1502 @return value of the property 1667 @return value of the property
1503 @rtype Any 1668 @rtype Any
1504 """ 1669 """
1505 if key == "INTERPRETER": 1670 if key == "INTERPRETER" and not FileSystemUtilities.isRemoteFileName(
1671 self.ppath
1672 ):
1506 return ( 1673 return (
1507 ericApp() 1674 ericApp()
1508 .getObject("VirtualEnvManager") 1675 .getObject("VirtualEnvManager")
1509 .getVirtualenvInterpreter(self.debugProperties["VIRTUALENV"]) 1676 .getVirtualenvInterpreter(self.debugProperties["VIRTUALENV"])
1510 ) 1677 )
1667 """ 1834 """
1668 tbPath = self.__pdata["TRANSLATIONSBINPATH"] 1835 tbPath = self.__pdata["TRANSLATIONSBINPATH"]
1669 for langFile in self.__pdata["TRANSLATIONS"][:]: 1836 for langFile in self.__pdata["TRANSLATIONS"][:]:
1670 qmFile = self.__binaryTranslationFile(langFile) 1837 qmFile = self.__binaryTranslationFile(langFile)
1671 if qmFile: 1838 if qmFile:
1672 if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists( 1839 if FileSystemUtilities.isRemoteFileName(self.ppath):
1673 os.path.join(self.ppath, qmFile) 1840 if qmFile not in self.__pdata[
1674 ): 1841 "TRANSLATIONS"
1675 self.appendFile(qmFile) 1842 ] and self.__remotefsInterface.exists(
1676 if tbPath: 1843 self.__remotefsInterface.join(self.ppath, qmFile)
1677 qmFile = os.path.join(tbPath, os.path.basename(qmFile)) 1844 ):
1845 self.appendFile(qmFile)
1846 if tbPath:
1847 qmFile = self.__remotefsInterface.join(
1848 tbPath, self.__remotefsInterface.basename(qmFile)
1849 )
1850 if qmFile not in self.__pdata[
1851 "TRANSLATIONS"
1852 ] and self.__remotefsInterface.exists(
1853 self.__remotefsInterface.join(self.ppath, qmFile)
1854 ):
1855 self.appendFile(qmFile)
1856 else:
1678 if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists( 1857 if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists(
1679 os.path.join(self.ppath, qmFile) 1858 os.path.join(self.ppath, qmFile)
1680 ): 1859 ):
1681 self.appendFile(qmFile) 1860 self.appendFile(qmFile)
1861 if tbPath:
1862 qmFile = os.path.join(tbPath, os.path.basename(qmFile))
1863 if qmFile not in self.__pdata[
1864 "TRANSLATIONS"
1865 ] and os.path.exists(os.path.join(self.ppath, qmFile)):
1866 self.appendFile(qmFile)
1682 1867
1683 def removeLanguageFile(self, langFile): 1868 def removeLanguageFile(self, langFile):
1684 """ 1869 """
1685 Public slot to remove a translation from the project. 1870 Public slot to remove a translation from the project.
1686 1871
1695 self.__model.removeItem(langFile) 1880 self.__model.removeItem(langFile)
1696 self.__pdata["TRANSLATIONS"].remove(langFile) 1881 self.__pdata["TRANSLATIONS"].remove(langFile)
1697 if qmFile: 1882 if qmFile:
1698 with contextlib.suppress(ValueError): 1883 with contextlib.suppress(ValueError):
1699 if self.__pdata["TRANSLATIONSBINPATH"]: 1884 if self.__pdata["TRANSLATIONSBINPATH"]:
1700 qmFile = self.getRelativePath( 1885 if FileSystemUtilities.isRemoteFileName(self.ppath):
1701 os.path.join( 1886 qmFile = self.__remotefsInterface.join(
1702 self.__pdata["TRANSLATIONSBINPATH"], 1887 self.__pdata["TRANSLATIONSBINPATH"],
1703 os.path.basename(qmFile), 1888 self.__remotefsInterface.basename(qmFile),
1704 ) 1889 )
1705 ) 1890 else:
1891 qmFile = self.getRelativePath(
1892 os.path.join(
1893 self.__pdata["TRANSLATIONSBINPATH"],
1894 os.path.basename(qmFile),
1895 )
1896 )
1706 self.__model.removeItem(qmFile) 1897 self.__model.removeItem(qmFile)
1707 self.__pdata["TRANSLATIONS"].remove(qmFile) 1898 self.__pdata["TRANSLATIONS"].remove(qmFile)
1708 self.setDirty(True) 1899 self.setDirty(True)
1709 1900
1710 def deleteLanguageFile(self, langFile): 1901 def deleteLanguageFile(self, langFile):
1716 """ 1907 """
1717 langFile = self.getRelativePath(langFile) 1908 langFile = self.getRelativePath(langFile)
1718 qmFile = self.__binaryTranslationFile(langFile) 1909 qmFile = self.__binaryTranslationFile(langFile)
1719 1910
1720 try: 1911 try:
1721 fn = os.path.join(self.ppath, langFile) 1912 if FileSystemUtilities.isRemoteFileName(self.ppath):
1722 if os.path.exists(fn): 1913 fn = self.__remotefsInterface.join(self.ppath, langFile)
1723 os.remove(fn) 1914 if self.__remotefsInterface.exists(fn):
1915 self.__remotefsInterface.remove(fn)
1916 else:
1917 fn = os.path.join(self.ppath, langFile)
1918 if os.path.exists(fn):
1919 os.remove(fn)
1724 except OSError as err: 1920 except OSError as err:
1725 EricMessageBox.critical( 1921 EricMessageBox.critical(
1726 self.ui, 1922 self.ui,
1727 self.tr("Delete translation"), 1923 self.tr("Delete Translation"),
1728 self.tr( 1924 self.tr(
1729 "<p>The selected translation file <b>{0}</b> could not be" 1925 "<p>The selected translation file <b>{0}</b> could not be"
1730 " deleted.</p><p>Reason: {1}</p>" 1926 " deleted.</p><p>Reason: {1}</p>"
1731 ).format(langFile, str(err)), 1927 ).format(langFile, str(err)),
1732 ) 1928 )
1773 """ 1969 """
1774 dirty = False 1970 dirty = False
1775 1971
1776 # make it relative to the project root, if it starts with that path 1972 # make it relative to the project root, if it starts with that path
1777 # assume relative paths are relative to the project root 1973 # assume relative paths are relative to the project root
1778 newfn = self.getRelativePath(fn) if os.path.isabs(fn) else fn 1974 if FileSystemUtilities.isRemoteFileName(self.ppath):
1779 newdir = os.path.dirname(newfn) 1975 newfn = self.getRelativePath(fn) if fn.startswith(self.ppath) else fn
1976 newdir = self.__remotefsInterface.dirname(newfn)
1977 else:
1978 newfn = self.getRelativePath(fn) if os.path.isabs(fn) else fn
1979 newdir = os.path.dirname(newfn)
1780 1980
1781 if isSourceFile: 1981 if isSourceFile:
1782 filetype = "SOURCES" 1982 filetype = "SOURCES"
1783 else: 1983 else:
1784 filetype = "OTHERS" 1984 filetype = "OTHERS"
1785 bfn = os.path.basename(newfn) 1985 bfn = (
1986 self.__remotefsInterface.basename(newfn)
1987 if FileSystemUtilities.isRemoteFileName(self.ppath)
1988 else os.path.basename(newfn)
1989 )
1786 if fnmatch.fnmatch(bfn, "*.ts") or fnmatch.fnmatch(bfn, "*.qm"): 1990 if fnmatch.fnmatch(bfn, "*.ts") or fnmatch.fnmatch(bfn, "*.qm"):
1787 filetype = "TRANSLATIONS" 1991 filetype = "TRANSLATIONS"
1788 else: 1992 else:
1789 for pattern in sorted(self.__pdata["FILETYPES"], reverse=True): 1993 for pattern in sorted(self.__pdata["FILETYPES"], reverse=True):
1790 if fnmatch.fnmatch(bfn, pattern): 1994 if fnmatch.fnmatch(bfn, pattern):
1821 self.__pdata["OTHERS"].append(newfn) 2025 self.__pdata["OTHERS"].append(newfn)
1822 self.othersAdded(newfn, updateModel) 2026 self.othersAdded(newfn, updateModel)
1823 dirty = True 2027 dirty = True
1824 else: 2028 else:
1825 updateModel and self.repopulateItem(newfn) 2029 updateModel and self.repopulateItem(newfn)
1826 if newdir not in self.otherssubdirs:
1827 self.otherssubdirs.append(newdir)
1828 2030
1829 if dirty: 2031 if dirty:
1830 self.setDirty(True) 2032 self.setDirty(True)
1831 2033
1832 @pyqtSlot() 2034 @pyqtSlot()
1846 2048
1847 dlg = AddFileDialog(self, self.parent(), fileTypeFilter, startdir=startdir) 2049 dlg = AddFileDialog(self, self.parent(), fileTypeFilter, startdir=startdir)
1848 if dlg.exec() == QDialog.DialogCode.Accepted: 2050 if dlg.exec() == QDialog.DialogCode.Accepted:
1849 fnames, target, isSource = dlg.getData() 2051 fnames, target, isSource = dlg.getData()
1850 if target != "": 2052 if target != "":
2053 isRemote = FileSystemUtilities.isRemoteFileName(target)
1851 for fn in fnames: 2054 for fn in fnames:
1852 targetfile = os.path.join(target, os.path.basename(fn)) 2055 targetfile = (
1853 if not FileSystemUtilities.samepath(os.path.dirname(fn), target): 2056 self.__remotefsInterface.join(
2057 target, self.__remotefsInterface.basename(fn)
2058 )
2059 if isRemote
2060 else os.path.join(target, os.path.basename(fn))
2061 )
2062 if not FileSystemUtilities.samepath(
2063 (
2064 self.__remotefsInterface.dirname(fn)
2065 if isRemote
2066 else os.path.dirname(fn)
2067 ),
2068 target,
2069 ):
1854 try: 2070 try:
1855 if not os.path.isdir(target): 2071 if isRemote:
1856 os.makedirs(target) 2072 if not self.__remotefsInterface.isdir(target):
1857 2073 self.__remotefsInterface.makedirs(target)
1858 if os.path.exists(targetfile): 2074 else:
2075 if not os.path.isdir(target):
2076 os.makedirs(target)
2077
2078 if (not isRemote and os.path.exists(targetfile)) or (
2079 isRemote and self.__remotefsInterface.exists(targetfile)
2080 ):
1859 res = EricMessageBox.yesNo( 2081 res = EricMessageBox.yesNo(
1860 self.ui, 2082 self.ui,
1861 self.tr("Add file"), 2083 self.tr("Add File"),
1862 self.tr( 2084 self.tr(
1863 "<p>The file <b>{0}</b> already" 2085 "<p>The file <b>{0}</b> already"
1864 " exists.</p><p>Overwrite it?</p>" 2086 " exists.</p><p>Overwrite it?</p>"
1865 ).format(targetfile), 2087 ).format(targetfile),
1866 icon=EricMessageBox.Warning, 2088 icon=EricMessageBox.Warning,
1867 ) 2089 )
1868 if not res: 2090 if not res:
1869 return # don't overwrite 2091 return # don't overwrite
1870 2092
1871 shutil.copy(fn, target) 2093 if isRemote:
2094 self.__remotefsInterface.shutilCopy(fn, target)
2095 else:
2096 shutil.copy(fn, target)
1872 except OSError as why: 2097 except OSError as why:
1873 EricMessageBox.critical( 2098 EricMessageBox.critical(
1874 self.ui, 2099 self.ui,
1875 self.tr("Add file"), 2100 self.tr("Add File"),
1876 self.tr( 2101 self.tr(
1877 "<p>The selected file <b>{0}</b> could" 2102 "<p>The selected file <b>{0}</b> could"
1878 " not be added to <b>{1}</b>.</p>" 2103 " not be added to <b>{1}</b>.</p>"
1879 "<p>Reason: {2}</p>" 2104 "<p>Reason: {2}</p>"
1880 ).format(fn, target, str(why)), 2105 ).format(fn, target, str(why)),
1911 patterns.append(pattern) 2136 patterns.append(pattern)
1912 elif patterntype == "__IGNORE__": 2137 elif patterntype == "__IGNORE__":
1913 ignorePatterns.append(pattern) 2138 ignorePatterns.append(pattern)
1914 2139
1915 files = [] 2140 files = []
2141 isRemote = FileSystemUtilities.isRemoteFileName(target)
1916 for pattern in patterns: 2142 for pattern in patterns:
1917 sstring = "{0}{1}{2}".format(source, os.sep, pattern) 2143 if isRemote:
1918 files.extend(glob.glob(sstring)) 2144 sstring = self.__remotefsInterface.join(source, pattern)
2145 files.extend(self.__remotefsInterface.glob(sstring))
2146 else:
2147 sstring = os.path.join(source, pattern)
2148 files.extend(glob.glob(sstring))
1919 2149
1920 if len(files) == 0: 2150 if len(files) == 0:
1921 if not quiet: 2151 if not quiet:
1922 EricMessageBox.information( 2152 EricMessageBox.information(
1923 self.ui, 2153 self.ui,
1924 self.tr("Add directory"), 2154 self.tr("Add Directory"),
1925 self.tr( 2155 self.tr(
1926 "<p>The source directory doesn't contain" 2156 "<p>The source directory doesn't contain"
1927 " any files belonging to the selected category.</p>" 2157 " any files belonging to the selected category.</p>"
1928 ), 2158 ),
1929 ) 2159 )
1930 return 2160 return
1931 2161
1932 if not FileSystemUtilities.samepath(target, source) and not os.path.isdir( 2162 if not FileSystemUtilities.samepath(target, source) and not (
1933 target 2163 (not isRemote and os.path.isdir(target))
2164 or (isRemote and self.__remotefsInterface.isdir(target))
1934 ): 2165 ):
1935 try: 2166 try:
1936 os.makedirs(target) 2167 if isRemote:
2168 self.__remotefsInterface.makedirs(target)
2169 else:
2170 os.makedirs(target)
1937 except OSError as why: 2171 except OSError as why:
1938 EricMessageBox.critical( 2172 EricMessageBox.critical(
1939 self.ui, 2173 self.ui,
1940 self.tr("Add directory"), 2174 self.tr("Add Directory"),
1941 self.tr( 2175 self.tr(
1942 "<p>The target directory <b>{0}</b> could not be" 2176 "<p>The target directory <b>{0}</b> could not be"
1943 " created.</p><p>Reason: {1}</p>" 2177 " created.</p><p>Reason: {1}</p>"
1944 ).format(target, str(why)), 2178 ).format(target, str(why)),
1945 ) 2179 )
1948 for file in files: 2182 for file in files:
1949 for pattern in ignorePatterns: 2183 for pattern in ignorePatterns:
1950 if fnmatch.fnmatch(file, pattern): 2184 if fnmatch.fnmatch(file, pattern):
1951 continue 2185 continue
1952 2186
1953 targetfile = os.path.join(target, os.path.basename(file)) 2187 targetfile = (
2188 self.__remotefsInterface.join(
2189 target, self.__remotefsInterface.basename(file)
2190 )
2191 if isRemote
2192 else os.path.join(target, os.path.basename(file))
2193 )
1954 if not FileSystemUtilities.samepath(target, source): 2194 if not FileSystemUtilities.samepath(target, source):
1955 try: 2195 try:
1956 if os.path.exists(targetfile): 2196 if (not isRemote and os.path.exists(targetfile)) or (
2197 isRemote and self.__remotefsInterface.exists(targetfile)
2198 ):
1957 res = EricMessageBox.yesNo( 2199 res = EricMessageBox.yesNo(
1958 self.ui, 2200 self.ui,
1959 self.tr("Add directory"), 2201 self.tr("Add Directory"),
1960 self.tr( 2202 self.tr(
1961 "<p>The file <b>{0}</b> already exists.</p>" 2203 "<p>The file <b>{0}</b> already exists.</p>"
1962 "<p>Overwrite it?</p>" 2204 "<p>Overwrite it?</p>"
1963 ).format(targetfile), 2205 ).format(targetfile),
1964 icon=EricMessageBox.Warning, 2206 icon=EricMessageBox.Warning,
1965 ) 2207 )
1966 if not res: 2208 if not res:
1967 continue 2209 continue
1968 # don't overwrite, carry on with next file 2210 # don't overwrite, carry on with next file
1969 2211
1970 shutil.copy(file, target) 2212 if isRemote:
2213 self.__remotefsInterface.shutilCopy(file, target)
2214 else:
2215 shutil.copy(file, target)
1971 except OSError: 2216 except OSError:
1972 continue 2217 continue
1973 self.appendFile(targetfile) 2218 self.appendFile(targetfile)
1974 2219
1975 def __addRecursiveDirectory(self, filetype, source, target): 2220 def __addRecursiveDirectory(self, filetype, source, target):
2001 pattern 2246 pattern
2002 for pattern, filetype in self.__pdata["FILETYPES"].items() 2247 for pattern, filetype in self.__pdata["FILETYPES"].items()
2003 if filetype == "__IGNORE__" 2248 if filetype == "__IGNORE__"
2004 ] 2249 ]
2005 2250
2006 # now recurse into subdirectories 2251 # now recurs into subdirectories
2007 with os.scandir(source) as dirEntriesIterator: 2252 if FileSystemUtilities.isRemoteFileName(target):
2008 for dirEntry in dirEntriesIterator: 2253 for entry in self.__remotefsInterface.listdir(source)[2]:
2009 if dirEntry.is_dir() and not any( 2254 if entry["is_dir"] and not any(
2010 fnmatch.fnmatch(dirEntry.name, ignore_pattern) 2255 fnmatch.fnmatch(entry["name"], ignore_pattern)
2011 for ignore_pattern in ignore_patterns 2256 for ignore_pattern in ignore_patterns
2012 ): 2257 ):
2013 self.__addRecursiveDirectory( 2258 self.__addRecursiveDirectory(
2014 filetype, dirEntry.path, os.path.join(target, dirEntry.name) 2259 filetype,
2260 entry["path"],
2261 self.__remotefsInterface.join(target, entry["name"]),
2015 ) 2262 )
2263 else:
2264 with os.scandir(source) as dirEntriesIterator:
2265 for dirEntry in dirEntriesIterator:
2266 if dirEntry.is_dir() and not any(
2267 fnmatch.fnmatch(dirEntry.name, ignore_pattern)
2268 for ignore_pattern in ignore_patterns
2269 ):
2270 self.__addRecursiveDirectory(
2271 filetype, dirEntry.path, os.path.join(target, dirEntry.name)
2272 )
2016 2273
2017 @pyqtSlot() 2274 @pyqtSlot()
2018 def addDirectory(self, fileTypeFilter=None, startdir=None): 2275 def addDirectory(self, fileTypeFilter=None, startdir=None):
2019 """ 2276 """
2020 Public method used to add all files of a directory to the project. 2277 Public method used to add all files of a directory to the project.
2063 2320
2064 @param fn file name or directory name to add 2321 @param fn file name or directory name to add
2065 @type str 2322 @type str
2066 """ 2323 """
2067 if fn: 2324 if fn:
2325 separator = (
2326 self.__remotefsInterface.separator()
2327 if FileSystemUtilities.isRemoteFileName(fn)
2328 else os.sep
2329 )
2330
2068 # if it is below the project directory, make it relative to that 2331 # if it is below the project directory, make it relative to that
2069 fn = self.getRelativePath(fn) 2332 fn = self.getRelativePath(fn)
2070 2333
2071 # if it ends with the directory separator character, remove it 2334 # if it ends with the directory separator character, remove it
2072 if fn.endswith(os.sep): 2335 if fn.endswith(separator):
2073 fn = fn[:-1] 2336 fn = fn[:-1]
2074 2337
2075 if fn not in self.__pdata["OTHERS"]: 2338 if fn not in self.__pdata["OTHERS"]:
2076 self.__pdata["OTHERS"].append(fn) 2339 self.__pdata["OTHERS"].append(fn)
2077 self.othersAdded(fn) 2340 self.othersAdded(fn)
2078 self.setDirty(True) 2341 self.setDirty(True)
2079 2342
2080 if os.path.isdir(fn) and fn not in self.otherssubdirs:
2081 self.otherssubdirs.append(fn)
2082
2083 def renameMainScript(self, oldfn, newfn): 2343 def renameMainScript(self, oldfn, newfn):
2084 """ 2344 """
2085 Public method to rename the main script. 2345 Public method to rename the main script.
2086 2346
2087 @param oldfn old filename 2347 @param oldfn old filename
2107 @param newfn new filename of the file 2367 @param newfn new filename of the file
2108 @type str 2368 @type str
2109 @return flag indicating success 2369 @return flag indicating success
2110 @rtype bool 2370 @rtype bool
2111 """ 2371 """
2372 isRemote = FileSystemUtilities.isRemoteFileName(oldfn)
2373
2112 fn = self.getRelativePath(oldfn) 2374 fn = self.getRelativePath(oldfn)
2113 isSourceFile = fn in self.__pdata["SOURCES"] 2375 isSourceFile = fn in self.__pdata["SOURCES"]
2114 2376
2115 if newfn is None: 2377 if newfn is None:
2116 newfn = EricFileDialog.getSaveFileName( 2378 if isRemote:
2117 None, 2379 newfn = EricServerFileDialog.getSaveFileName(
2118 self.tr("Rename file"), 2380 None,
2119 oldfn, 2381 self.tr("Rename File"),
2120 "", 2382 oldfn,
2121 options=EricFileDialog.DontConfirmOverwrite, 2383 "",
2122 ) 2384 )
2385 else:
2386 newfn = EricFileDialog.getSaveFileName(
2387 None,
2388 self.tr("Rename File"),
2389 oldfn,
2390 "",
2391 options=EricFileDialog.DontConfirmOverwrite,
2392 )
2393 if newfn:
2394 newfn = FileSystemUtilities.toNativeSeparators(newfn)
2395
2123 if not newfn: 2396 if not newfn:
2124 return False 2397 return False
2125 newfn = FileSystemUtilities.toNativeSeparators(newfn) 2398
2126 2399 if (not isRemote and os.path.exists(newfn)) or (
2127 if os.path.exists(newfn): 2400 isRemote and self.__remotefsInterface.exists(newfn)
2401 ):
2128 res = EricMessageBox.yesNo( 2402 res = EricMessageBox.yesNo(
2129 self.ui, 2403 self.ui,
2130 self.tr("Rename File"), 2404 self.tr("Rename File"),
2131 self.tr( 2405 self.tr(
2132 """<p>The file <b>{0}</b> already exists.""" 2406 """<p>The file <b>{0}</b> already exists."""
2136 ) 2410 )
2137 if not res: 2411 if not res:
2138 return False 2412 return False
2139 2413
2140 try: 2414 try:
2141 os.rename(oldfn, newfn) 2415 if isRemote:
2416 self.__remotefsInterface.replace(oldfn, newfn)
2417 else:
2418 os.rename(oldfn, newfn)
2142 except OSError as msg: 2419 except OSError as msg:
2143 EricMessageBox.critical( 2420 EricMessageBox.critical(
2144 self.ui, 2421 self.ui,
2145 self.tr("Rename File"), 2422 self.tr("Rename File"),
2146 self.tr( 2423 self.tr(
2165 @type str 2442 @type str
2166 @param isSourceFile flag indicating that this is a source file 2443 @param isSourceFile flag indicating that this is a source file
2167 even if it doesn't have the source extension 2444 even if it doesn't have the source extension
2168 @type bool 2445 @type bool
2169 """ 2446 """
2447 if FileSystemUtilities.isRemoteFileName(oldname):
2448 oldDirName = self.__remotefsInterface.dirname(oldname)
2449 newDirName = self.__remotefsInterface.dirname(newname)
2450 else:
2451 oldDirName = os.path.dirname(oldname)
2452 newDirName = os.path.dirname(newname)
2453
2170 fn = self.getRelativePath(oldname) 2454 fn = self.getRelativePath(oldname)
2171 if os.path.dirname(oldname) == os.path.dirname(newname): 2455 if oldDirName == newDirName:
2172 if self.__isInPdata(oldname): 2456 if self.__isInPdata(oldname):
2173 self.removeFile(oldname, False) 2457 self.removeFile(oldname, False)
2174 self.appendFile(newname, isSourceFile, False) 2458 self.appendFile(newname, isSourceFile, False)
2175 self.__model.renameItem(fn, newname) 2459 self.__model.renameItem(fn, newname)
2176 else: 2460 else:
2187 @param start prefix 2471 @param start prefix
2188 @type str 2472 @type str
2189 @return list of files starting with a common prefix 2473 @return list of files starting with a common prefix
2190 @rtype list of str 2474 @rtype list of str
2191 """ 2475 """
2476 isRemote = FileSystemUtilities.isRemoteFileName(self.ppath)
2477
2192 filelist = [] 2478 filelist = []
2193 start = self.getRelativePath(start) 2479 start = self.getRelativePath(start)
2194 for fileCategory in [ 2480 for fileCategory in [
2195 c for c in self.getFileCategories() if c != "TRANSLATIONS" 2481 c for c in self.getFileCategories() if c != "TRANSLATIONS"
2196 ]: 2482 ]:
2197 for entry in self.__pdata[fileCategory][:]: 2483 for entry in self.__pdata[fileCategory][:]:
2198 if entry.startswith(start): 2484 if entry.startswith(start):
2199 filelist.append(os.path.join(self.ppath, entry)) 2485 filelist.append(
2486 self.__remotefsInterface.join(self.ppath, entry)
2487 if isRemote
2488 else os.path.join(self.ppath, entry)
2489 )
2200 return filelist 2490 return filelist
2201 2491
2202 def __reorganizeFiles(self): 2492 def __reorganizeFiles(self):
2203 """ 2493 """
2204 Private method to reorganize files stored in the project. 2494 Private method to reorganize files stored in the project.
2205 """ 2495 """
2206 reorganized = False 2496 reorganized = False
2207 2497 isRemote = FileSystemUtilities.isRemoteFileName(self.ppath)
2208 # init data store for the reorganization 2498
2499 # initialize data store for the reorganization
2209 newPdata = {} 2500 newPdata = {}
2210 for fileCategory in self.getFileCategories(): 2501 for fileCategory in self.getFileCategories():
2211 newPdata[fileCategory] = [] 2502 newPdata[fileCategory] = []
2212 2503
2213 # iterate over all files checking for a reassignment 2504 # iterate over all files checking for a reassignment
2214 for fileCategory in self.getFileCategories(): 2505 for fileCategory in self.getFileCategories():
2215 for fn in self.__pdata[fileCategory][:]: 2506 for fn in self.__pdata[fileCategory][:]:
2216 filetype = fileCategory 2507 filetype = fileCategory
2217 bfn = os.path.basename(fn) 2508 bfn = (
2509 self.__remotefsInterface.basename(fn)
2510 if isRemote
2511 else os.path.basename(fn)
2512 )
2218 for pattern in sorted(self.__pdata["FILETYPES"], reverse=True): 2513 for pattern in sorted(self.__pdata["FILETYPES"], reverse=True):
2219 if fnmatch.fnmatch(bfn, pattern): 2514 if fnmatch.fnmatch(bfn, pattern):
2220 filetype = self.__pdata["FILETYPES"][pattern] 2515 filetype = self.__pdata["FILETYPES"][pattern]
2221 break 2516 break
2222 2517
2241 @param olddn original directory name 2536 @param olddn original directory name
2242 @type str 2537 @type str
2243 @param newdn new directory name 2538 @param newdn new directory name
2244 @type str 2539 @type str
2245 """ 2540 """
2541 isRemote = FileSystemUtilities.isRemoteFileName(self.ppath)
2542
2246 olddn = self.getRelativePath(olddn) 2543 olddn = self.getRelativePath(olddn)
2247 newdn = self.getRelativePath(newdn) 2544 newdn = self.getRelativePath(newdn)
2248 for fileCategory in [ 2545 for fileCategory in [
2249 c for c in self.getFileCategories() if c != "TRANSLATIONS" 2546 c for c in self.getFileCategories() if c != "TRANSLATIONS"
2250 ]: 2547 ]:
2251 for entry in self.__pdata[fileCategory][:]: 2548 for entry in self.__pdata[fileCategory][:]:
2252 if entry.startswith(olddn): 2549 if entry.startswith(olddn):
2253 entry = entry.replace(olddn, newdn) 2550 entry = entry.replace(olddn, newdn)
2254 self.appendFile( 2551 self.appendFile(
2255 os.path.join(self.ppath, entry), fileCategory == "SOURCES" 2552 (
2553 self.__remotefsInterface.join(self.ppath, entry)
2554 if isRemote
2555 else os.path.join(self.ppath, entry)
2556 ),
2557 fileCategory == "SOURCES",
2256 ) 2558 )
2257 self.setDirty(True) 2559 self.setDirty(True)
2258 2560
2259 def moveDirectory(self, olddn, newdn): 2561 def moveDirectory(self, olddn, newdn):
2260 """ 2562 """
2276 if fileCategory not in typeStrings: 2578 if fileCategory not in typeStrings:
2277 typeStrings.append(fileCategory) 2579 typeStrings.append(fileCategory)
2278 self.__pdata[fileCategory].remove(entry) 2580 self.__pdata[fileCategory].remove(entry)
2279 entry = entry.replace(olddn, newdn) 2581 entry = entry.replace(olddn, newdn)
2280 self.__pdata[fileCategory].append(entry) 2582 self.__pdata[fileCategory].append(entry)
2281 if fileCategory == "OTHERS": 2583 if fileCategory != "OTHERS" and newdn not in self.subdirs:
2282 if newdn not in self.otherssubdirs: 2584 self.subdirs.append(newdn)
2283 self.otherssubdirs.append(newdn)
2284 else:
2285 if newdn not in self.subdirs:
2286 self.subdirs.append(newdn)
2287 if typeStrings: 2585 if typeStrings:
2288 # the directory is controlled by the project 2586 # the directory is controlled by the project
2289 self.setDirty(True) 2587 self.setDirty(True)
2290 self.__model.removeItem(olddn) 2588 self.__model.removeItem(olddn)
2291 typeString = typeStrings[0] 2589 typeString = typeStrings[0]
2323 The directory is not deleted from the project directory. 2621 The directory is not deleted from the project directory.
2324 2622
2325 @param dn directory name to be removed from the project 2623 @param dn directory name to be removed from the project
2326 @type str 2624 @type str
2327 """ 2625 """
2626 separator = (
2627 self.__remotefsInterface.separator()
2628 if FileSystemUtilities.isRemoteFileName(self.ppath)
2629 else os.sep
2630 )
2631
2328 dirty = False 2632 dirty = False
2329 dn = self.getRelativePath(dn) 2633 dn = self.getRelativePath(dn)
2330 for entry in self.__pdata["OTHERS"][:]: 2634 for entry in self.__pdata["OTHERS"][:]:
2331 if entry.startswith(dn): 2635 if entry.startswith(dn):
2332 self.__pdata["OTHERS"].remove(entry) 2636 self.__pdata["OTHERS"].remove(entry)
2333 dirty = True 2637 dirty = True
2334 dn2 = dn if dn.endswith(os.sep) else dn + os.sep 2638 dn2 = dn if dn.endswith(separator) else dn + separator
2335 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: 2639 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]:
2336 for entry in self.__pdata[fileCategory][:]: 2640 for entry in self.__pdata[fileCategory][:]:
2337 if entry.startswith(dn2): 2641 if entry.startswith(dn2):
2338 self.__pdata[fileCategory].remove(entry) 2642 self.__pdata[fileCategory].remove(entry)
2339 dirty = True 2643 dirty = True
2350 @type str 2654 @type str
2351 @return flag indicating success 2655 @return flag indicating success
2352 @rtype bool 2656 @rtype bool
2353 """ 2657 """
2354 try: 2658 try:
2355 os.remove(os.path.join(self.ppath, fn)) 2659 if FileSystemUtilities.isRemoteFileName(self.ppath):
2356 path, ext = os.path.splitext(fn) 2660 self.__remotefsInterface.remove(
2357 if ext == ".ui": 2661 self.__remotefsInterface.join(self.ppath, fn)
2358 fn2 = os.path.join(self.ppath, "{0}.h".format(fn))
2359 if os.path.isfile(fn2):
2360 os.remove(fn2)
2361 head, tail = os.path.split(path)
2362 for ext in [".pyc", ".pyo"]:
2363 fn2 = os.path.join(self.ppath, path + ext)
2364 if os.path.isfile(fn2):
2365 os.remove(fn2)
2366 pat = os.path.join(
2367 self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext)
2368 ) 2662 )
2369 for f in glob.glob(pat): 2663 filepath = self.__remotefsInterface.splitext(fn)[0]
2370 os.remove(f) 2664 head, tail = self.__remotefsInterface.split(filepath)
2665 for ext in [".pyc", ".pyo"]:
2666 fn2 = self.__remotefsInterface.join(self.ppath, filepath + ext)
2667 if self.__remotefsInterface.isfile(fn2):
2668 self.__remotefsInterface.remove(fn2)
2669 pat = self.__remotefsInterface.join(
2670 self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext)
2671 )
2672 for f in self.__remotefsInterface.glob(pat):
2673 self.__remotefsInterface.remove(f)
2674 else:
2675 os.remove(os.path.join(self.ppath, fn))
2676 filepath = os.path.splitext(fn)[0]
2677 head, tail = os.path.split(filepath)
2678 for ext in [".pyc", ".pyo"]:
2679 fn2 = os.path.join(self.ppath, filepath + ext)
2680 if os.path.isfile(fn2):
2681 os.remove(fn2)
2682 pat = os.path.join(
2683 self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext)
2684 )
2685 for f in glob.glob(pat):
2686 os.remove(f)
2371 except OSError as err: 2687 except OSError as err:
2372 EricMessageBox.critical( 2688 EricMessageBox.critical(
2373 self.ui, 2689 self.ui,
2374 self.tr("Delete file"), 2690 self.tr("Delete File"),
2375 self.tr( 2691 self.tr(
2376 "<p>The selected file <b>{0}</b> could not be" 2692 "<p>The selected file <b>{0}</b> could not be"
2377 " deleted.</p><p>Reason: {1}</p>" 2693 " deleted.</p><p>Reason: {1}</p>"
2378 ).format(fn, str(err)), 2694 ).format(fn, str(err)),
2379 ) 2695 )
2380 return False 2696 return False
2381 2697
2382 self.removeFile(fn) 2698 self.removeFile(fn)
2383 if ext == ".ui":
2384 self.removeFile(fn + ".h")
2385 return True 2699 return True
2386 2700
2387 def deleteDirectory(self, dn): 2701 def deleteDirectory(self, dn):
2388 """ 2702 """
2389 Public method to delete a directory from the project directory. 2703 Public method to delete a directory from the project directory.
2391 @param dn directory name to be removed from the project 2705 @param dn directory name to be removed from the project
2392 @type str 2706 @type str
2393 @return flag indicating success 2707 @return flag indicating success
2394 @rtype bool 2708 @rtype bool
2395 """ 2709 """
2396 if not os.path.isabs(dn): 2710 dn = self.getAbsolutePath(dn)
2397 dn = os.path.join(self.ppath, dn)
2398 try: 2711 try:
2399 shutil.rmtree(dn, ignore_errors=True) 2712 if FileSystemUtilities.isRemoteFileName(dn):
2713 self.__remotefsInterface.shutilRmtree(dn, ignore_errors=True)
2714 else:
2715 shutil.rmtree(dn, ignore_errors=True)
2400 except OSError as err: 2716 except OSError as err:
2401 EricMessageBox.critical( 2717 EricMessageBox.critical(
2402 self.ui, 2718 self.ui,
2403 self.tr("Delete directory"), 2719 self.tr("Delete Directory"),
2404 self.tr( 2720 self.tr(
2405 "<p>The selected directory <b>{0}</b> could not be" 2721 "<p>The selected directory <b>{0}</b> could not be"
2406 " deleted.</p><p>Reason: {1}</p>" 2722 " deleted.</p><p>Reason: {1}</p>"
2407 ).format(dn, str(err)), 2723 ).format(dn, str(err)),
2408 ) 2724 )
2432 Public slot to built a new project. 2748 Public slot to built a new project.
2433 2749
2434 This method displays the new project dialog and initializes 2750 This method displays the new project dialog and initializes
2435 the project object with the data entered. 2751 the project object with the data entered.
2436 """ 2752 """
2753 # assume remote project without VCS if connected to server
2437 from eric7.VCS.CommandOptionsDialog import VcsCommandOptionsDialog 2754 from eric7.VCS.CommandOptionsDialog import VcsCommandOptionsDialog
2438 2755
2439 from .PropertiesDialog import PropertiesDialog 2756 from .PropertiesDialog import PropertiesDialog
2440 2757
2441 if not self.checkDirty(): 2758 if not self.checkDirty():
2442 return 2759 return
2443 2760
2444 dlg = PropertiesDialog(self, new=True) 2761 isRemote = self.__remoteServer.isServerConnected()
2762
2763 dlg = PropertiesDialog(self, new=True, isRemote=isRemote)
2445 if dlg.exec() == QDialog.DialogCode.Accepted: 2764 if dlg.exec() == QDialog.DialogCode.Accepted:
2446 self.closeProject() 2765 self.closeProject()
2447 2766
2448 # reset the auto save flag 2767 # reset the auto save flag
2449 autoSaveProject = Preferences.getProject("AutoSaveProject") 2768 autoSaveProject = Preferences.getProject("AutoSaveProject")
2456 self.initFileTypes() 2775 self.initFileTypes()
2457 self.setDirty(True) 2776 self.setDirty(True)
2458 self.reloadAct.setEnabled(True) 2777 self.reloadAct.setEnabled(True)
2459 self.closeAct.setEnabled(True) 2778 self.closeAct.setEnabled(True)
2460 self.saveasAct.setEnabled(True) 2779 self.saveasAct.setEnabled(True)
2780 self.saveasRemoteAct.setEnabled(
2781 self.__remoteServer.isServerConnected()
2782 and FileSystemUtilities.isRemoteFileName(self.pfile)
2783 )
2461 self.actGrp2.setEnabled(True) 2784 self.actGrp2.setEnabled(True)
2462 self.propsAct.setEnabled(True) 2785 self.propsAct.setEnabled(True)
2463 self.userPropsAct.setEnabled(True) 2786 self.userPropsAct.setEnabled(not isRemote)
2464 self.filetypesAct.setEnabled(True) 2787 self.filetypesAct.setEnabled(True)
2465 self.lexersAct.setEnabled(True) 2788 self.lexersAct.setEnabled(True)
2466 self.sessActGrp.setEnabled(False) 2789 self.sessActGrp.setEnabled(False)
2467 self.dbgActGrp.setEnabled(True) 2790 self.dbgActGrp.setEnabled(True)
2468 self.menuDebuggerAct.setEnabled(True) 2791 self.menuDebuggerAct.setEnabled(True)
2469 self.menuSessionAct.setEnabled(False) 2792 self.menuSessionAct.setEnabled(False)
2470 self.menuCheckAct.setEnabled(True) 2793 self.menuCheckAct.setEnabled(True)
2471 self.menuShowAct.setEnabled(True) 2794 self.menuShowAct.setEnabled(True)
2472 self.menuDiagramAct.setEnabled(True) 2795 self.menuDiagramAct.setEnabled(True)
2473 self.menuApidocAct.setEnabled(True) 2796 self.menuApidocAct.setEnabled(
2797 not FileSystemUtilities.isRemoteFileName(self.ppath)
2798 )
2474 self.menuPackagersAct.setEnabled(True) 2799 self.menuPackagersAct.setEnabled(True)
2475 self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"]) 2800 self.pluginGrp.setEnabled(
2801 self.__pdata["PROJECTTYPE"] in ["E7Plugin"]
2802 and not FileSystemUtilities.isRemoteFileName(self.ppath)
2803 )
2476 self.addLanguageAct.setEnabled(bool(self.__pdata["TRANSLATIONPATTERN"])) 2804 self.addLanguageAct.setEnabled(bool(self.__pdata["TRANSLATIONPATTERN"]))
2477 self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) 2805 self.makeGrp.setEnabled(
2478 self.menuMakeAct.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) 2806 self.__pdata["MAKEPARAMS"]["MakeEnabled"]
2807 and not FileSystemUtilities.isRemoteFileName(self.ppath)
2808 )
2809 self.menuMakeAct.setEnabled(
2810 self.__pdata["MAKEPARAMS"]["MakeEnabled"]
2811 and not FileSystemUtilities.isRemoteFileName(self.ppath)
2812 )
2479 self.menuOtherToolsAct.setEnabled(True) 2813 self.menuOtherToolsAct.setEnabled(True)
2480 self.menuFormattingAct.setEnabled(True) 2814 self.menuFormattingAct.setEnabled(
2815 not FileSystemUtilities.isRemoteFileName(self.ppath)
2816 )
2817 self.menuVcsAct.setEnabled(
2818 not FileSystemUtilities.isRemoteFileName(self.ppath)
2819 )
2481 2820
2482 self.projectAboutToBeCreated.emit() 2821 self.projectAboutToBeCreated.emit()
2483 2822
2484 hashStr = str( 2823 hashStr = str(
2485 QCryptographicHash.hash( 2824 QCryptographicHash.hash(
2495 self.__pdata["LEXERASSOCS"] = { 2834 self.__pdata["LEXERASSOCS"] = {
2496 "*.py": "MicroPython", 2835 "*.py": "MicroPython",
2497 } 2836 }
2498 2837
2499 # create the project directory if it doesn't exist already 2838 # create the project directory if it doesn't exist already
2500 if not os.path.isdir(self.ppath): 2839 ppathExists = (
2840 self.__remotefsInterface.isdir(self.ppath)
2841 if isRemote
2842 else os.path.isdir(self.ppath)
2843 )
2844 if not ppathExists:
2501 try: 2845 try:
2502 os.makedirs(self.ppath) 2846 if isRemote:
2847 self.__remotefsInterface.makedirs(self.ppath)
2848 else:
2849 os.makedirs(self.ppath)
2503 except OSError: 2850 except OSError:
2504 EricMessageBox.critical( 2851 EricMessageBox.critical(
2505 self.ui, 2852 self.ui,
2506 self.tr("Create project directory"), 2853 self.tr("Create project directory"),
2507 self.tr( 2854 self.tr(
2515 return 2862 return
2516 2863
2517 # create an empty __init__.py file to make it a Python package 2864 # create an empty __init__.py file to make it a Python package
2518 # (only for Python and Python3) 2865 # (only for Python and Python3)
2519 if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: 2866 if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]:
2520 fn = os.path.join(self.ppath, "__init__.py") 2867 if isRemote:
2521 with open(fn, "w", encoding="utf-8"): 2868 fn = self.__remotefsInterface.join(self.ppath, "__init__.py")
2522 pass 2869 self.__remotefsInterface.writeFile(fn, b"")
2870 else:
2871 fn = os.path.join(self.ppath, "__init__.py")
2872 with open(fn, "w", encoding="utf-8"):
2873 pass
2523 self.appendFile(fn, True) 2874 self.appendFile(fn, True)
2524 2875
2525 # create an empty main script file, if a name was given 2876 # create an empty main script file, if a name was given
2526 if self.__pdata["MAINSCRIPT"]: 2877 if self.__pdata["MAINSCRIPT"]:
2527 if not os.path.isabs(self.__pdata["MAINSCRIPT"]): 2878 if isRemote:
2528 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 2879 if not self.__remotefsInterface.isabs(
2880 self.__pdata["MAINSCRIPT"]
2881 ):
2882 ms = self.__remotefsInterface.join(
2883 self.ppath, self.__pdata["MAINSCRIPT"]
2884 )
2885 else:
2886 ms = self.__pdata["MAINSCRIPT"]
2887 self.__remotefsInterface.makedirs(
2888 self.__remotefsInterface.dirname(ms), exist_ok=True
2889 )
2890 self.__remotefsInterface.writeFile(ms, b"")
2529 else: 2891 else:
2530 ms = self.__pdata["MAINSCRIPT"] 2892 if not os.path.isabs(self.__pdata["MAINSCRIPT"]):
2531 os.makedirs(os.path.dirname(ms), exist_ok=True) 2893 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
2532 with open(ms, "w"): 2894 else:
2533 pass 2895 ms = self.__pdata["MAINSCRIPT"]
2896 os.makedirs(os.path.dirname(ms), exist_ok=True)
2897 with open(ms, "w"):
2898 pass
2534 self.appendFile(ms, True) 2899 self.appendFile(ms, True)
2535 2900
2536 if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: 2901 if self.__pdata["MAKEPARAMS"]["MakeEnabled"] and not isRemote:
2537 mf = self.__pdata["MAKEPARAMS"]["MakeFile"] 2902 mf = self.__pdata["MAKEPARAMS"]["MakeFile"]
2538 if mf: 2903 if mf:
2539 if not os.path.isabs(mf): 2904 if not os.path.isabs(mf):
2540 mf = os.path.join(self.ppath, mf) 2905 mf = os.path.join(self.ppath, mf)
2541 else: 2906 else:
2543 os.makedirs(os.path.dirname(mf), exist_ok=True) 2908 os.makedirs(os.path.dirname(mf), exist_ok=True)
2544 with open(mf, "w"): 2909 with open(mf, "w"):
2545 pass 2910 pass
2546 self.appendFile(mf) 2911 self.appendFile(mf)
2547 2912
2548 tpd = os.path.join(self.ppath, self.translationsRoot) 2913 if isRemote:
2549 if not self.translationsRoot.endswith(os.sep): 2914 tpd = self.__remotefsInterface.join(
2550 tpd = os.path.dirname(tpd) 2915 self.ppath, self.translationsRoot
2551 if not os.path.isdir(tpd): 2916 )
2552 os.makedirs(tpd, exist_ok=True) 2917 if not self.translationsRoot.endswith(
2553 if self.__pdata["TRANSLATIONSBINPATH"]: 2918 self.__remotefsInterface.separator()
2554 tpd = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) 2919 ):
2920 tpd = self.__remotefsInterface.dirname(tpd)
2921 if not self.__remotefsInterface.isdir(tpd):
2922 self.__remotefsInterface.makedirs(tpd, exist_ok=True)
2923 if self.__pdata["TRANSLATIONSBINPATH"]:
2924 tpd = self.__remotefsInterface.join(
2925 self.ppath, self.__pdata["TRANSLATIONSBINPATH"]
2926 )
2927 if not self.__remotefsInterface.isdir(tpd):
2928 self.__remotefsInterface.makedirs(tpd, exist_ok=True)
2929 else:
2930 tpd = os.path.join(self.ppath, self.translationsRoot)
2931 if not self.translationsRoot.endswith(os.sep):
2932 tpd = os.path.dirname(tpd)
2555 if not os.path.isdir(tpd): 2933 if not os.path.isdir(tpd):
2556 os.makedirs(tpd, exist_ok=True) 2934 os.makedirs(tpd, exist_ok=True)
2935 if self.__pdata["TRANSLATIONSBINPATH"]:
2936 tpd = os.path.join(
2937 self.ppath, self.__pdata["TRANSLATIONSBINPATH"]
2938 )
2939 if not os.path.isdir(tpd):
2940 os.makedirs(tpd, exist_ok=True)
2557 2941
2558 # create management directory if not present 2942 # create management directory if not present
2559 self.createProjectManagementDir() 2943 self.createProjectManagementDir()
2560 2944
2561 self.saveProject() 2945 self.saveProject()
2576 # set the auto save flag to its supposed value 2960 # set the auto save flag to its supposed value
2577 Preferences.setProject("AutoSaveProject", autoSaveProject) 2961 Preferences.setProject("AutoSaveProject", autoSaveProject)
2578 return 2962 return
2579 2963
2580 if self.__pdata["MAINSCRIPT"]: 2964 if self.__pdata["MAINSCRIPT"]:
2581 if not os.path.isabs(self.__pdata["MAINSCRIPT"]): 2965 if isRemote:
2582 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 2966 if not self.__remotefsInterface.isabs(
2967 self.__pdata["MAINSCRIPT"]
2968 ):
2969 ms = self.__remotefsInterface.join(
2970 self.ppath, self.__pdata["MAINSCRIPT"]
2971 )
2972 else:
2973 ms = self.__pdata["MAINSCRIPT"]
2974 msExists = self.__remotefsInterface.exists(ms)
2583 else: 2975 else:
2584 ms = self.__pdata["MAINSCRIPT"] 2976 if not os.path.isabs(self.__pdata["MAINSCRIPT"]):
2585 if not os.path.exists(ms): 2977 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
2978 else:
2979 ms = self.__pdata["MAINSCRIPT"]
2980 msExists = os.path.exists(ms)
2981 if not msExists:
2586 try: 2982 try:
2587 os.makedirs(os.path.dirname(ms), exist_ok=True) 2983 if isRemote:
2588 with open(ms, "w"): 2984 self.__remotefsInterface.makedirs(
2589 pass 2985 self.__remotefsInterface.dirname(ms), exist_ok=True
2986 )
2987 self.__remotefsInterface.writeFile(ms, b"")
2988 else:
2989 os.makedirs(os.path.dirname(ms), exist_ok=True)
2990 with open(ms, "w"):
2991 pass
2590 except OSError as err: 2992 except OSError as err:
2591 EricMessageBox.critical( 2993 EricMessageBox.critical(
2592 self.ui, 2994 self.ui,
2593 self.tr("Create main script"), 2995 self.tr("Create main script"),
2594 self.tr( 2996 self.tr(
2595 "<p>The mainscript <b>{0}</b> could not" 2997 "<p>The main script <b>{0}</b> could not"
2596 " be created.<br/>Reason: {1}</p>" 2998 " be created.<br/>Reason: {1}</p>"
2597 ).format(ms, str(err)), 2999 ).format(ms, str(err)),
2598 ) 3000 )
2599 self.appendFile(ms, True) 3001 self.appendFile(ms, True)
2600 else: 3002 else:
2601 ms = "" 3003 ms = ""
2602 3004
2603 if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: 3005 if self.__pdata["MAKEPARAMS"]["MakeEnabled"] and not isRemote:
2604 mf = self.__pdata["MAKEPARAMS"]["MakeFile"] 3006 mf = self.__pdata["MAKEPARAMS"]["MakeFile"]
2605 if mf: 3007 if mf:
2606 if not os.path.isabs(mf): 3008 if not os.path.isabs(mf):
2607 mf = os.path.join(self.ppath, mf) 3009 mf = os.path.join(self.ppath, mf)
2608 else: 3010 else:
2629 self.tr("New Project"), 3031 self.tr("New Project"),
2630 self.tr("""Add existing files to the project?"""), 3032 self.tr("""Add existing files to the project?"""),
2631 yesDefault=True, 3033 yesDefault=True,
2632 ) 3034 )
2633 if res: 3035 if res:
2634 self.newProjectAddFiles(ms) 3036 self.newProjectAddFiles(ms, isRemote=isRemote)
2635 addAllToVcs = res 3037 addAllToVcs = res and not isRemote
3038
2636 # create an empty __init__.py file to make it a Python package 3039 # create an empty __init__.py file to make it a Python package
2637 # if none exists (only for Python and Python3) 3040 # if none exists (only for Python and Python3)
2638 if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: 3041 if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]:
2639 fn = os.path.join(self.ppath, "__init__.py") 3042 if isRemote:
2640 if not os.path.exists(fn): 3043 fn = self.__remotefsInterface.join(self.ppath, "__init__.py")
2641 with open(fn, "w", encoding="utf-8"): 3044 if not self.__remotefsInterface.exists(fn):
2642 pass 3045 self.__remotefsInterface.writeFile(fn, b"")
2643 self.appendFile(fn, True) 3046 self.appendFile(fn, True)
3047 else:
3048 fn = os.path.join(self.ppath, "__init__.py")
3049 if not os.path.exists(fn):
3050 with open(fn, "w", encoding="utf-8"):
3051 pass
3052 self.appendFile(fn, True)
2644 self.saveProject() 3053 self.saveProject()
2645 3054
2646 # check, if the existing project directory is already under 3055 # check, if the existing project directory is already under
2647 # VCS control 3056 # VCS control
2648 pluginManager = ericApp().getObject("PluginManager") 3057 if not isRemote:
2649 for indicator, vcsData in list( 3058 pluginManager = ericApp().getObject("PluginManager")
2650 pluginManager.getVcsSystemIndicators().items() 3059 for indicator, vcsData in list(
2651 ): 3060 pluginManager.getVcsSystemIndicators().items()
2652 if os.path.exists(os.path.join(self.ppath, indicator)): 3061 ):
2653 if len(vcsData) > 1: 3062 if os.path.exists(os.path.join(self.ppath, indicator)):
2654 vcsList = [] 3063 if len(vcsData) > 1:
2655 for _vcsSystemStr, vcsSystemDisplay in vcsData: 3064 vcsList = []
2656 vcsList.append(vcsSystemDisplay) 3065 for _vcsSystemStr, vcsSystemDisplay in vcsData:
2657 res, vcs_ok = QInputDialog.getItem( 3066 vcsList.append(vcsSystemDisplay)
2658 None, 3067 res, vcs_ok = QInputDialog.getItem(
2659 self.tr("New Project"), 3068 None,
2660 self.tr("Select Version Control System"), 3069 self.tr("New Project"),
2661 vcsList, 3070 self.tr("Select Version Control System"),
2662 0, 3071 vcsList,
2663 False, 3072 0,
2664 ) 3073 False,
2665 if vcs_ok: 3074 )
2666 for vcsSystemStr, vcsSystemDisplay in vcsData: 3075 if vcs_ok:
2667 if res == vcsSystemDisplay: 3076 for vcsSystemStr, vcsSystemDisplay in vcsData:
2668 vcsSystem = vcsSystemStr 3077 if res == vcsSystemDisplay:
2669 break 3078 vcsSystem = vcsSystemStr
3079 break
3080 else:
3081 vcsSystem = "None"
2670 else: 3082 else:
2671 vcsSystem = "None" 3083 vcsSystem = "None"
2672 else: 3084 else:
2673 vcsSystem = "None" 3085 vcsSystem = vcsData[0][1]
2674 else: 3086 self.__pdata["VCS"] = vcsSystem
2675 vcsSystem = vcsData[0][1] 3087 self.vcs = self.initVCS()
2676 self.__pdata["VCS"] = vcsSystem 3088 self.setDirty(True)
2677 self.vcs = self.initVCS() 3089 if self.vcs is not None:
2678 self.setDirty(True) 3090 # edit VCS command options
2679 if self.vcs is not None: 3091 if self.vcs.vcsSupportCommandOptions():
2680 # edit VCS command options 3092 vcores = EricMessageBox.yesNo(
2681 if self.vcs.vcsSupportCommandOptions(): 3093 self.ui,
2682 vcores = EricMessageBox.yesNo( 3094 self.tr("New Project"),
2683 self.ui, 3095 self.tr(
2684 self.tr("New Project"), 3096 """Would you like to edit the VCS"""
2685 self.tr( 3097 """ command options?"""
2686 """Would you like to edit the VCS""" 3098 ),
2687 """ command options?""" 3099 )
2688 ), 3100 else:
2689 ) 3101 vcores = False
3102 if vcores:
3103 codlg = VcsCommandOptionsDialog(self.vcs)
3104 if codlg.exec() == QDialog.DialogCode.Accepted:
3105 self.vcs.vcsSetOptions(codlg.getOptions())
3106 # add project file to repository
3107 if res == 0:
3108 apres = EricMessageBox.yesNo(
3109 self.ui,
3110 self.tr("New Project"),
3111 self.tr(
3112 "Shall the project file be added"
3113 " to the repository?"
3114 ),
3115 yesDefault=True,
3116 )
3117 if apres:
3118 self.saveProject()
3119 self.vcs.vcsAdd(self.pfile)
2690 else: 3120 else:
2691 vcores = False 3121 self.__pdata["VCS"] = "None"
2692 if vcores: 3122 self.saveProject()
2693 codlg = VcsCommandOptionsDialog(self.vcs) 3123 break
2694 if codlg.exec() == QDialog.DialogCode.Accepted:
2695 self.vcs.vcsSetOptions(codlg.getOptions())
2696 # add project file to repository
2697 if res == 0:
2698 apres = EricMessageBox.yesNo(
2699 self.ui,
2700 self.tr("New project"),
2701 self.tr(
2702 "Shall the project file be added"
2703 " to the repository?"
2704 ),
2705 yesDefault=True,
2706 )
2707 if apres:
2708 self.saveProject()
2709 self.vcs.vcsAdd(self.pfile)
2710 else:
2711 self.__pdata["VCS"] = "None"
2712 self.saveProject()
2713 break
2714 3124
2715 # put the project under VCS control 3125 # put the project under VCS control
2716 if self.vcs is None and self.vcsSoftwareAvailable() and self.vcsRequested: 3126 if (
3127 not isRemote
3128 and self.vcs is None
3129 and self.vcsSoftwareAvailable()
3130 and self.vcsRequested
3131 ):
2717 vcsSystemsDict = ( 3132 vcsSystemsDict = (
2718 ericApp() 3133 ericApp()
2719 .getObject("PluginManager") 3134 .getObject("PluginManager")
2720 .getPluginDisplayStrings("version_control") 3135 .getPluginDisplayStrings("version_control")
2721 ) 3136 )
2779 self.newProject.emit() 3194 self.newProject.emit()
2780 3195
2781 # set the auto save flag to its supposed value 3196 # set the auto save flag to its supposed value
2782 Preferences.setProject("AutoSaveProject", autoSaveProject) 3197 Preferences.setProject("AutoSaveProject", autoSaveProject)
2783 3198
2784 if self.__pdata["EMBEDDED_VENV"]: 3199 if self.__pdata[
3200 "EMBEDDED_VENV"
3201 ] and not FileSystemUtilities.isRemoteFileName(self.ppath):
2785 self.__createEmbeddedEnvironment() 3202 self.__createEmbeddedEnvironment()
2786 self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) 3203 self.menuEnvironmentAct.setEnabled(
3204 self.__pdata["EMBEDDED_VENV"]
3205 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3206 )
2787 3207
2788 self.projectOpenedHooks.emit() 3208 self.projectOpenedHooks.emit()
2789 self.projectOpened.emit() 3209 self.projectOpened.emit()
2790 3210
2791 # open the main script 3211 # open the main script
2792 if self.__pdata["MAINSCRIPT"]: 3212 if self.__pdata["MAINSCRIPT"]:
2793 if not os.path.isabs(self.__pdata["MAINSCRIPT"]): 3213 if isRemote:
2794 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 3214 if not self.__remotefsInterface.isabs(self.__pdata["MAINSCRIPT"]):
3215 ms = self.__remotefsInterface.join(
3216 self.ppath, self.__pdata["MAINSCRIPT"]
3217 )
3218 else:
3219 ms = self.__pdata["MAINSCRIPT"]
2795 else: 3220 else:
2796 ms = self.__pdata["MAINSCRIPT"] 3221 if not os.path.isabs(self.__pdata["MAINSCRIPT"]):
3222 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
3223 else:
3224 ms = self.__pdata["MAINSCRIPT"]
2797 self.sourceFile.emit(ms) 3225 self.sourceFile.emit(ms)
2798 3226
2799 def newProjectAddFiles(self, mainscript): 3227 def newProjectAddFiles(self, mainscript, isRemote=False):
2800 """ 3228 """
2801 Public method to add files to a new project. 3229 Public method to add files to a new project.
2802 3230
2803 @param mainscript name of the mainscript 3231 @param mainscript name of the mainscript
2804 @type str 3232 @type str
3233 @param isRemote flag indicating a remote project (defaults to False)
3234 @type bool (optional)
2805 """ 3235 """
2806 # Show the file type associations for the user to change 3236 # Show the file type associations for the user to change
2807 self.__showFiletypeAssociations() 3237 self.__showFiletypeAssociations()
2808 3238
2809 ignoreList = [] 3239 ignoreList = []
2810 if self.__pdata["EMBEDDED_VENV"]: 3240 if self.__pdata["EMBEDDED_VENV"]:
2811 environmentPath = self.__findEmbeddedEnvironment() 3241 environmentPath = self.__findEmbeddedEnvironment()
2812 if environmentPath: 3242 if environmentPath:
2813 # there is already an embeded venv; ignore thie whenn adding files 3243 # there is already an embedded venv; ignore this when adding files
2814 ignoreList = [os.path.split(environmentPath)[-1]] 3244 ignoreList = (
3245 [self.__remotefsInterface.split(environmentPath)[-1]]
3246 if isRemote
3247 else [os.path.split(environmentPath)[-1]]
3248 )
2815 with EricOverrideCursor(): 3249 with EricOverrideCursor():
2816 # search the project directory for files with known extensions 3250 # search the project directory for files with known extensions
2817 for filespec in self.__pdata["FILETYPES"]: 3251 for filespec in self.__pdata["FILETYPES"]:
2818 files = FileSystemUtilities.direntries( 3252 files = (
2819 self.ppath, 3253 self.__remotefsInterface.direntries(
2820 filesonly=True, 3254 self.ppath,
2821 pattern=filespec, 3255 filesonly=True,
2822 ignore=ignoreList, 3256 pattern=filespec,
3257 ignore=ignoreList,
3258 )
3259 if isRemote
3260 else FileSystemUtilities.direntries(
3261 self.ppath,
3262 filesonly=True,
3263 pattern=filespec,
3264 ignore=ignoreList,
3265 )
2823 ) 3266 )
2824 for file in files: 3267 for file in files:
2825 self.appendFile(file) 3268 self.appendFile(file)
2826 3269
2827 # special handling for translation files 3270 # special handling for translation files
2828 if self.translationsRoot: 3271 if self.translationsRoot:
2829 tpd = os.path.join(self.ppath, self.translationsRoot) 3272 if isRemote:
2830 if not self.translationsRoot.endswith(os.sep): 3273 tpd = self.__remotefsInterface.join(
2831 tpd = os.path.dirname(tpd) 3274 self.ppath, self.translationsRoot
3275 )
3276 if not self.translationsRoot.endswith(os.sep):
3277 tpd = self.__remotefsInterface.dirname(tpd)
3278 else:
3279 tpd = os.path.join(self.ppath, self.translationsRoot)
3280 if not self.translationsRoot.endswith(os.sep):
3281 tpd = os.path.dirname(tpd)
2832 else: 3282 else:
2833 tpd = self.ppath 3283 tpd = self.ppath
2834 tslist = [] 3284 tslist = []
2835 if self.__pdata["TRANSLATIONPATTERN"]: 3285 if self.__pdata["TRANSLATIONPATTERN"]:
2836 pattern = os.path.basename(self.__pdata["TRANSLATIONPATTERN"]) 3286 pattern = (
3287 self.__remotefsInterface.basename(
3288 self.__pdata["TRANSLATIONPATTERN"]
3289 )
3290 if isRemote
3291 else os.path.basename(self.__pdata["TRANSLATIONPATTERN"])
3292 )
2837 if "%language%" in pattern: 3293 if "%language%" in pattern:
2838 pattern = pattern.replace("%language%", "*") 3294 pattern = pattern.replace("%language%", "*")
2839 else: 3295 else:
2840 tpd = self.__pdata["TRANSLATIONPATTERN"].split("%language%")[0] 3296 tpd = self.__pdata["TRANSLATIONPATTERN"].split("%language%")[0]
2841 else: 3297 else:
2842 pattern = "*.ts" 3298 pattern = "*.ts"
2843 tslist.extend(FileSystemUtilities.direntries(tpd, True, pattern)) 3299 tslist.extend(
3300 self.__remotefsInterface.direntries(tpd, True, pattern)
3301 if isRemote
3302 else FileSystemUtilities.direntries(tpd, True, pattern)
3303 )
3304
2844 pattern = self.__binaryTranslationFile(pattern) 3305 pattern = self.__binaryTranslationFile(pattern)
2845 if pattern: 3306 if pattern:
2846 tslist.extend(FileSystemUtilities.direntries(tpd, True, pattern)) 3307 tslist.extend(
3308 self.__remotefsInterface.direntries(tpd, True, pattern)
3309 if isRemote
3310 else FileSystemUtilities.direntries(tpd, True, pattern)
3311 )
2847 if tslist: 3312 if tslist:
2848 if "_" in os.path.basename(tslist[0]): 3313 hasUnderscore = (
2849 # the first entry determines the mainscript name 3314 "_" in self.__remotefsInterface.basename(tslist[0])
2850 mainscriptname = ( 3315 if isRemote
2851 os.path.splitext(mainscript)[0] 3316 else "_" in os.path.basename(tslist[0])
2852 or os.path.basename(tslist[0]).split("_")[0] 3317 )
2853 ) 3318 if hasUnderscore:
2854 self.__pdata["TRANSLATIONPATTERN"] = os.path.join( 3319 # the first entry determines the main script name
2855 os.path.dirname(tslist[0]), 3320 if isRemote:
2856 "{0}_%language%{1}".format( 3321 mainscriptname = (
2857 os.path.basename(tslist[0]).split("_")[0], 3322 self.__remotefsInterface.splitext(mainscript)[0]
2858 os.path.splitext(tslist[0])[1], 3323 or self.__remotefsInterface.basename(tslist[0]).split("_")[
2859 ), 3324 0
2860 ) 3325 ]
3326 )
3327 self.__pdata["TRANSLATIONPATTERN"] = (
3328 self.__remotefsInterface.join(
3329 self.__remotefsInterface.dirname(tslist[0]),
3330 "{0}_%language%{1}".format(
3331 self.__remotefsInterface.basename(tslist[0]).split(
3332 "_"
3333 )[0],
3334 self.__remotefsInterface.splitext(tslist[0])[1],
3335 ),
3336 )
3337 )
3338 else:
3339 mainscriptname = (
3340 os.path.splitext(mainscript)[0]
3341 or os.path.basename(tslist[0]).split("_")[0]
3342 )
3343 self.__pdata["TRANSLATIONPATTERN"] = os.path.join(
3344 os.path.dirname(tslist[0]),
3345 "{0}_%language%{1}".format(
3346 os.path.basename(tslist[0]).split("_")[0],
3347 os.path.splitext(tslist[0])[1],
3348 ),
3349 )
2861 else: 3350 else:
2862 mainscriptname = "" 3351 mainscriptname = ""
2863 pattern, ok = QInputDialog.getText( 3352 pattern, ok = QInputDialog.getText(
2864 None, 3353 None,
2865 self.tr("Translation Pattern"), 3354 self.tr("Translation Pattern"),
2883 for ts in tslist: 3372 for ts in tslist:
2884 if fnmatch.fnmatch(ts, pattern): 3373 if fnmatch.fnmatch(ts, pattern):
2885 self.__pdata["TRANSLATIONS"].append(ts) 3374 self.__pdata["TRANSLATIONS"].append(ts)
2886 self.projectFileAdded.emit(ts, "TRANSLATIONS") 3375 self.projectFileAdded.emit(ts, "TRANSLATIONS")
2887 if self.__pdata["TRANSLATIONSBINPATH"]: 3376 if self.__pdata["TRANSLATIONSBINPATH"]:
2888 tpd = os.path.join( 3377 if isRemote:
2889 self.ppath, self.__pdata["TRANSLATIONSBINPATH"] 3378 tpd = self.__remotefsInterface.join(
2890 ) 3379 self.ppath, self.__pdata["TRANSLATIONSBINPATH"]
2891 pattern = os.path.basename( 3380 )
2892 self.__pdata["TRANSLATIONPATTERN"] 3381 pattern = self.__remotefsInterface.basename(
2893 ).replace("%language%", "*") 3382 self.__pdata["TRANSLATIONPATTERN"]
3383 ).replace("%language%", "*")
3384 else:
3385 tpd = os.path.join(
3386 self.ppath, self.__pdata["TRANSLATIONSBINPATH"]
3387 )
3388 pattern = os.path.basename(
3389 self.__pdata["TRANSLATIONPATTERN"]
3390 ).replace("%language%", "*")
2894 pattern = self.__binaryTranslationFile(pattern) 3391 pattern = self.__binaryTranslationFile(pattern)
2895 qmlist = FileSystemUtilities.direntries(tpd, True, pattern) 3392 qmlist = FileSystemUtilities.direntries(tpd, True, pattern)
2896 for qm in qmlist: 3393 for qm in qmlist:
2897 self.__pdata["TRANSLATIONS"].append(qm) 3394 self.__pdata["TRANSLATIONS"].append(qm)
2898 self.projectFileAdded.emit(qm, "TRANSLATIONS") 3395 self.projectFileAdded.emit(qm, "TRANSLATIONS")
2907 """ 3404 """
2908 Private slot to display the properties dialog. 3405 Private slot to display the properties dialog.
2909 """ 3406 """
2910 from .PropertiesDialog import PropertiesDialog 3407 from .PropertiesDialog import PropertiesDialog
2911 3408
2912 dlg = PropertiesDialog(self, new=False) 3409 isRemote = FileSystemUtilities.isRemoteFileName(self.ppath)
3410 dlg = PropertiesDialog(self, new=False, isRemote=isRemote)
2913 if dlg.exec() == QDialog.DialogCode.Accepted: 3411 if dlg.exec() == QDialog.DialogCode.Accepted:
2914 fileTypesDict = copy.copy(self.__pdata["FILETYPES"]) 3412 fileTypesDict = copy.copy(self.__pdata["FILETYPES"])
2915 dlg.storeData() 3413 dlg.storeData()
2916 self.setDirty(True) 3414 self.setDirty(True)
2917 if self.__pdata["MAINSCRIPT"]: 3415 if self.__pdata["MAINSCRIPT"]:
2918 if not os.path.isabs(self.__pdata["MAINSCRIPT"]): 3416 if isRemote:
2919 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 3417 if not self.__remotefsInterface.isabs(self.__pdata["MAINSCRIPT"]):
3418 ms = self.__remotefsInterface.join(
3419 self.ppath, self.__pdata["MAINSCRIPT"]
3420 )
3421 else:
3422 ms = self.__pdata["MAINSCRIPT"]
3423 if self.__remotefsInterface.exists(ms):
3424 self.appendFile(ms)
2920 else: 3425 else:
2921 ms = self.__pdata["MAINSCRIPT"] 3426 if not os.path.isabs(self.__pdata["MAINSCRIPT"]):
2922 if os.path.exists(ms): 3427 ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
2923 self.appendFile(ms) 3428 else:
3429 ms = self.__pdata["MAINSCRIPT"]
3430 if os.path.exists(ms):
3431 self.appendFile(ms)
2924 3432
2925 if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: 3433 if self.__pdata["MAKEPARAMS"]["MakeEnabled"]:
2926 mf = self.__pdata["MAKEPARAMS"]["MakeFile"] 3434 mf = self.__pdata["MAKEPARAMS"]["MakeFile"]
2927 if mf: 3435 if isRemote:
2928 if not os.path.isabs(mf): 3436 if mf:
2929 mf = os.path.join(self.ppath, mf) 3437 if not self.__remotefsInterface.isabs(mf):
3438 mf = self.__remotefsInterface.join(self.ppath, mf)
3439 else:
3440 mf = self.__remotefsInterface.join(
3441 self.ppath, Project.DefaultMakefile
3442 )
3443 exists = self.__remotefsInterface.exists(mf)
2930 else: 3444 else:
2931 mf = os.path.join(self.ppath, Project.DefaultMakefile) 3445 if mf:
2932 if not os.path.exists(mf): 3446 if not os.path.isabs(mf):
3447 mf = os.path.join(self.ppath, mf)
3448 else:
3449 mf = os.path.join(self.ppath, Project.DefaultMakefile)
3450 exists = os.path.exists(mf)
3451 if not exists:
2933 try: 3452 try:
2934 with open(mf, "w"): 3453 if isRemote:
2935 pass 3454 self.__remotefsInterface.writeFile(mf, b"")
3455 else:
3456 with open(mf, "w"):
3457 pass
2936 except OSError as err: 3458 except OSError as err:
2937 EricMessageBox.critical( 3459 EricMessageBox.critical(
2938 self.ui, 3460 self.ui,
2939 self.tr("Create Makefile"), 3461 self.tr("Create Makefile"),
2940 self.tr( 3462 self.tr(
2943 ).format(mf, str(err)), 3465 ).format(mf, str(err)),
2944 ) 3466 )
2945 self.appendFile(mf) 3467 self.appendFile(mf)
2946 3468
2947 if self.translationsRoot: 3469 if self.translationsRoot:
2948 tp = os.path.join(self.ppath, self.translationsRoot) 3470 if isRemote:
2949 if not self.translationsRoot.endswith(os.sep): 3471 tp = self.__remotefsInterface.join(
2950 tp = os.path.dirname(tp) 3472 self.ppath, self.translationsRoot
3473 )
3474 if not self.translationsRoot.endswith(
3475 self.__remotefsInterface.separator()
3476 ):
3477 tp = self.__remotefsInterface.dirname(tp)
3478 if not self.__remotefsInterface.isdir(tp):
3479 self.__remotefsInterface.makedirs(tp)
3480 else:
3481 tp = os.path.join(self.ppath, self.translationsRoot)
3482 if not self.translationsRoot.endswith(os.sep):
3483 tp = os.path.dirname(tp)
3484 if not os.path.isdir(tp):
3485 os.makedirs(tp)
2951 else: 3486 else:
2952 tp = self.ppath 3487 tp = self.ppath
2953 if not os.path.isdir(tp):
2954 os.makedirs(tp)
2955 if tp != self.ppath and tp not in self.subdirs: 3488 if tp != self.ppath and tp not in self.subdirs:
2956 self.subdirs.append(tp) 3489 self.subdirs.append(tp)
2957 3490
2958 if self.__pdata["TRANSLATIONSBINPATH"]: 3491 if self.__pdata["TRANSLATIONSBINPATH"]:
2959 tp = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) 3492 if isRemote:
2960 if not os.path.isdir(tp): 3493 tp = self.__remotefsInterface.join(
2961 os.makedirs(tp) 3494 self.ppath, self.__pdata["TRANSLATIONSBINPATH"]
3495 )
3496 if not self.__remotefsInterface.isdir(tp):
3497 self.__remotefsInterface.makedirs(tp)
3498 else:
3499 tp = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"])
3500 if not os.path.isdir(tp):
3501 os.makedirs(tp)
2962 if tp != self.ppath and tp not in self.subdirs: 3502 if tp != self.ppath and tp not in self.subdirs:
2963 self.subdirs.append(tp) 3503 self.subdirs.append(tp)
2964 3504
2965 self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"]) 3505 self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"])
2966 3506
2968 self.projectPropertiesChanged.emit() 3508 self.projectPropertiesChanged.emit()
2969 3509
2970 if self.__pdata["FILETYPES"] != fileTypesDict: 3510 if self.__pdata["FILETYPES"] != fileTypesDict:
2971 self.__reorganizeFiles() 3511 self.__reorganizeFiles()
2972 3512
2973 if self.__pdata["EMBEDDED_VENV"] and not self.__findEmbeddedEnvironment(): 3513 if (
3514 self.__pdata["EMBEDDED_VENV"]
3515 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3516 and not self.__findEmbeddedEnvironment()
3517 ):
2974 self.__createEmbeddedEnvironment() 3518 self.__createEmbeddedEnvironment()
2975 3519
2976 def __showUserProperties(self): 3520 def __showUserProperties(self):
2977 """ 3521 """
2978 Private slot to display the user specific properties dialog. 3522 Private slot to display the user specific properties dialog.
3108 return 3652 return
3109 3653
3110 if fn is None: 3654 if fn is None:
3111 fn = EricFileDialog.getOpenFileName( 3655 fn = EricFileDialog.getOpenFileName(
3112 self.parent(), 3656 self.parent(),
3113 self.tr("Open project"), 3657 self.tr("Open Project"),
3114 Preferences.getMultiProject("Workspace") or OSUtilities.getHomeDir(), 3658 Preferences.getMultiProject("Workspace") or OSUtilities.getHomeDir(),
3115 self.tr("Project Files (*.epj)"), 3659 self.tr("Project Files (*.epj)"),
3116 ) 3660 )
3117 3661
3118 if fn and self.closeProject(): 3662 if fn and self.closeProject():
3143 self.__readUserProperties() 3687 self.__readUserProperties()
3144 3688
3145 with EricOverrideCursor(): 3689 with EricOverrideCursor():
3146 oldState = self.isDirty() 3690 oldState = self.isDirty()
3147 self.vcs = self.initVCS() 3691 self.vcs = self.initVCS()
3148 if self.vcs is None and self.isDirty() == oldState: 3692 if not FileSystemUtilities.isRemoteFileName(self.ppath):
3149 # check, if project is version controlled 3693 if self.vcs is None and self.isDirty() == oldState:
3150 pluginManager = ericApp().getObject("PluginManager") 3694 # check, if project is version controlled
3151 for ( 3695 pluginManager = ericApp().getObject("PluginManager")
3152 indicator, 3696 for (
3153 vcsData, 3697 indicator,
3154 ) in pluginManager.getVcsSystemIndicators().items(): 3698 vcsData,
3155 if os.path.exists(os.path.join(self.ppath, indicator)): 3699 ) in pluginManager.getVcsSystemIndicators().items():
3156 if len(vcsData) > 1: 3700 if os.path.exists(os.path.join(self.ppath, indicator)):
3157 vcsList = [] 3701 if len(vcsData) > 1:
3158 for _vcsSystemStr, vcsSystemDisplay in vcsData: 3702 vcsList = []
3159 vcsList.append(vcsSystemDisplay) 3703 for _vcsSystemStr, vcsSystemDisplay in vcsData:
3160 with EricOverridenCursor(): 3704 vcsList.append(vcsSystemDisplay)
3161 res, vcs_ok = QInputDialog.getItem( 3705 with EricOverridenCursor():
3162 None, 3706 res, vcs_ok = QInputDialog.getItem(
3163 self.tr("New Project"), 3707 None,
3164 self.tr("Select Version Control System"), 3708 self.tr("New Project"),
3165 vcsList, 3709 self.tr(
3166 0, 3710 "Select Version Control System"
3167 False, 3711 ),
3168 ) 3712 vcsList,
3169 if vcs_ok: 3713 0,
3170 for vcsSystemStr, vcsSystemDisplay in vcsData: 3714 False,
3171 if res == vcsSystemDisplay: 3715 )
3172 vcsSystem = vcsSystemStr 3716 if vcs_ok:
3173 break 3717 for (
3718 vcsSystemStr,
3719 vcsSystemDisplay,
3720 ) in vcsData:
3721 if res == vcsSystemDisplay:
3722 vcsSystem = vcsSystemStr
3723 break
3724 else:
3725 vcsSystem = "None"
3174 else: 3726 else:
3175 vcsSystem = "None" 3727 vcsSystem = "None"
3176 else: 3728 else:
3177 vcsSystem = "None" 3729 vcsSystem = vcsData[0][0]
3178 else: 3730 self.__pdata["VCS"] = vcsSystem
3179 vcsSystem = vcsData[0][0] 3731 self.vcs = self.initVCS()
3180 self.__pdata["VCS"] = vcsSystem 3732 self.setDirty(True)
3181 self.vcs = self.initVCS() 3733 if self.vcs is not None and (
3182 self.setDirty(True) 3734 self.vcs.vcsRegisteredState(self.ppath)
3183 if self.vcs is not None and ( 3735 != VersionControlState.Controlled
3184 self.vcs.vcsRegisteredState(self.ppath) 3736 ):
3185 != VersionControlState.Controlled 3737 self.__pdata["VCS"] = "None"
3186 ): 3738 self.vcs = self.initVCS()
3187 self.__pdata["VCS"] = "None"
3188 self.vcs = self.initVCS()
3189 self.reloadAct.setEnabled(True) 3739 self.reloadAct.setEnabled(True)
3190 self.closeAct.setEnabled(True) 3740 self.closeAct.setEnabled(True)
3191 self.saveasAct.setEnabled(True) 3741 self.saveasAct.setEnabled(True)
3742 self.saveasRemoteAct.setEnabled(
3743 self.__remoteServer.isServerConnected()
3744 and FileSystemUtilities.isRemoteFileName(self.pfile)
3745 )
3192 self.actGrp2.setEnabled(True) 3746 self.actGrp2.setEnabled(True)
3193 self.propsAct.setEnabled(True) 3747 self.propsAct.setEnabled(True)
3194 self.userPropsAct.setEnabled(True) 3748 self.userPropsAct.setEnabled(
3749 not FileSystemUtilities.isRemoteFileName(self.pfile)
3750 )
3195 self.filetypesAct.setEnabled(True) 3751 self.filetypesAct.setEnabled(True)
3196 self.lexersAct.setEnabled(True) 3752 self.lexersAct.setEnabled(True)
3197 self.sessActGrp.setEnabled(True) 3753 self.sessActGrp.setEnabled(True)
3198 self.dbgActGrp.setEnabled(True) 3754 self.dbgActGrp.setEnabled(True)
3199 self.menuDebuggerAct.setEnabled(True) 3755 self.menuDebuggerAct.setEnabled(True)
3200 self.menuSessionAct.setEnabled(True) 3756 self.menuSessionAct.setEnabled(True)
3201 self.menuCheckAct.setEnabled(True) 3757 self.menuCheckAct.setEnabled(True)
3202 self.menuShowAct.setEnabled(True) 3758 self.menuShowAct.setEnabled(True)
3203 self.menuDiagramAct.setEnabled(True) 3759 self.menuDiagramAct.setEnabled(True)
3204 self.menuApidocAct.setEnabled(True) 3760 self.menuApidocAct.setEnabled(
3205 self.menuPackagersAct.setEnabled(True) 3761 not FileSystemUtilities.isRemoteFileName(self.ppath)
3762 )
3763 self.menuPackagersAct.setEnabled(
3764 not FileSystemUtilities.isRemoteFileName(self.ppath)
3765 )
3206 self.pluginGrp.setEnabled( 3766 self.pluginGrp.setEnabled(
3207 self.__pdata["PROJECTTYPE"] in ["E7Plugin"] 3767 self.__pdata["PROJECTTYPE"] in ["E7Plugin"]
3768 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3208 ) 3769 )
3209 self.addLanguageAct.setEnabled( 3770 self.addLanguageAct.setEnabled(
3210 bool(self.__pdata["TRANSLATIONPATTERN"]) 3771 bool(self.__pdata["TRANSLATIONPATTERN"])
3211 ) 3772 )
3212 self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) 3773 self.makeGrp.setEnabled(
3774 self.__pdata["MAKEPARAMS"]["MakeEnabled"]
3775 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3776 )
3213 self.menuMakeAct.setEnabled( 3777 self.menuMakeAct.setEnabled(
3214 self.__pdata["MAKEPARAMS"]["MakeEnabled"] 3778 self.__pdata["MAKEPARAMS"]["MakeEnabled"]
3779 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3215 ) 3780 )
3216 self.menuOtherToolsAct.setEnabled(True) 3781 self.menuOtherToolsAct.setEnabled(True)
3217 self.menuFormattingAct.setEnabled(True) 3782 self.menuFormattingAct.setEnabled(
3783 not FileSystemUtilities.isRemoteFileName(self.ppath)
3784 )
3785 self.menuVcsAct.setEnabled(
3786 not FileSystemUtilities.isRemoteFileName(self.ppath)
3787 )
3218 3788
3219 # open a project debugger properties file being quiet 3789 # open a project debugger properties file being quiet
3220 # about errors 3790 # about errors
3221 if Preferences.getProject("AutoLoadDbgProperties"): 3791 if Preferences.getProject("AutoLoadDbgProperties"):
3222 self.__readDebugProperties(True) 3792 self.__readDebugProperties(True)
3223 3793
3224 self.__model.projectOpened() 3794 self.__model.projectOpened()
3225 3795
3226 if self.__pdata["EMBEDDED_VENV"]: 3796 if self.__pdata[
3797 "EMBEDDED_VENV"
3798 ] and not FileSystemUtilities.isRemoteFileName(self.ppath):
3227 envPath = self.__findEmbeddedEnvironment() 3799 envPath = self.__findEmbeddedEnvironment()
3228 if bool(envPath): 3800 if bool(envPath):
3229 self.__loadEnvironmentConfiguration() 3801 self.__loadEnvironmentConfiguration()
3230 if not bool( 3802 if not bool(
3231 self.__venvConfiguration["interpreter"] 3803 self.__venvConfiguration["interpreter"]
3233 self.__venvConfiguration["interpreter"], os.X_OK 3805 self.__venvConfiguration["interpreter"], os.X_OK
3234 ): 3806 ):
3235 self.__configureEnvironment(envPath) 3807 self.__configureEnvironment(envPath)
3236 else: 3808 else:
3237 self.__createEmbeddedEnvironment() 3809 self.__createEmbeddedEnvironment()
3238 self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) 3810 self.menuEnvironmentAct.setEnabled(
3811 self.__pdata["EMBEDDED_VENV"]
3812 and not FileSystemUtilities.isRemoteFileName(self.ppath)
3813 )
3239 3814
3240 self.projectOpenedHooks.emit() 3815 self.projectOpenedHooks.emit()
3241 self.projectOpened.emit() 3816 self.projectOpened.emit()
3242 3817
3243 if Preferences.getProject("SearchNewFiles"): 3818 if Preferences.getProject("SearchNewFiles"):
3264 self.__readSession(quiet=True, indicator="_tmp") 3839 self.__readSession(quiet=True, indicator="_tmp")
3265 elif Preferences.getProject("AutoLoadSession"): 3840 elif Preferences.getProject("AutoLoadSession"):
3266 self.__readSession(quiet=True) 3841 self.__readSession(quiet=True)
3267 3842
3268 # start the VCS monitor thread 3843 # start the VCS monitor thread
3269 self.__vcsConnectStatusMonitor() 3844 if not FileSystemUtilities.isRemoteFileName(self.ppath):
3845 self.__vcsConnectStatusMonitor()
3270 else: 3846 else:
3271 self.__initData() # delete all invalid data read 3847 self.__initData() # delete all invalid data read
3272 3848
3273 def reopenProject(self): 3849 def reopenProject(self):
3274 """ 3850 """
3326 if ex: 3902 if ex:
3327 fpath = fpath.with_suffix(ex) 3903 fpath = fpath.with_suffix(ex)
3328 if fpath.exists(): 3904 if fpath.exists():
3329 res = EricMessageBox.yesNo( 3905 res = EricMessageBox.yesNo(
3330 self.ui, 3906 self.ui,
3331 self.tr("Save File"), 3907 self.tr("Save Project"),
3332 self.tr( 3908 self.tr(
3333 """<p>The file <b>{0}</b> already exists.""" 3909 """<p>The file <b>{0}</b> already exists."""
3334 """ Overwrite it?</p>""" 3910 """ Overwrite it?</p>"""
3335 ).format(fpath), 3911 ).format(fpath),
3336 icon=EricMessageBox.Warning, 3912 icon=EricMessageBox.Warning,
3337 ) 3913 )
3338 if not res: 3914 if not res:
3339 return False 3915 return False
3340 3916
3341 self.name = fpath.stem
3342 ok = self.__writeProject(str(fpath)) 3917 ok = self.__writeProject(str(fpath))
3343 3918
3344 if ok: 3919 if ok:
3345 # create management directory if not present 3920 # create management directory if not present
3346 self.createProjectManagementDir() 3921 self.createProjectManagementDir()
3449 success &= vm.closeWindow(fn, ignoreDirty=True) 4024 success &= vm.closeWindow(fn, ignoreDirty=True)
3450 if not success: 4025 if not success:
3451 return False 4026 return False
3452 4027
3453 # stop the VCS monitor thread 4028 # stop the VCS monitor thread
3454 if self.vcs is not None: 4029 if (
4030 not FileSystemUtilities.isRemoteFileName(self.ppath)
4031 and self.vcs is not None
4032 ):
3455 self.vcs.stopStatusMonitor() 4033 self.vcs.stopStatusMonitor()
3456 4034
3457 # now save the tasks 4035 # now save the tasks
3458 if not noSave: 4036 if not noSave:
3459 self.writeTasks() 4037 self.writeTasks()
3460 self.ui.taskViewer.clearProjectTasks() 4038 self.ui.taskViewer.clearProjectTasks()
3461 self.ui.taskViewer.setProjectOpen(False) 4039 self.ui.taskViewer.setProjectOpen(False)
3462 4040
4041 if FileSystemUtilities.isRemoteFileName(self.ppath):
4042 self.__remotefsInterface.removeFromFsCache(self.ppath)
4043
3463 # now shutdown the vcs interface 4044 # now shutdown the vcs interface
3464 if self.vcs: 4045 if not FileSystemUtilities.isRemoteFileName(self.ppath) and self.vcs:
3465 self.vcs.vcsShutdown() 4046 self.vcs.vcsShutdown()
3466 self.vcs.deleteLater() 4047 self.vcs.deleteLater()
3467 self.vcs = None 4048 self.vcs = None
3468 ericApp().getObject("PluginManager").deactivateVcsPlugins() 4049 ericApp().getObject("PluginManager").deactivateVcsPlugins()
3469 4050
3471 self.__closeAllWindows() 4052 self.__closeAllWindows()
3472 4053
3473 self.__initData() 4054 self.__initData()
3474 self.reloadAct.setEnabled(False) 4055 self.reloadAct.setEnabled(False)
3475 self.closeAct.setEnabled(False) 4056 self.closeAct.setEnabled(False)
4057 self.saveasRemoteAct.setEnabled(False)
3476 self.saveasAct.setEnabled(False) 4058 self.saveasAct.setEnabled(False)
3477 self.saveAct.setEnabled(False) 4059 self.saveAct.setEnabled(False)
3478 self.actGrp2.setEnabled(False) 4060 self.actGrp2.setEnabled(False)
3479 self.propsAct.setEnabled(False) 4061 self.propsAct.setEnabled(False)
3480 self.userPropsAct.setEnabled(False) 4062 self.userPropsAct.setEnabled(False)
3524 filesWithSyntaxErrors += 1 4106 filesWithSyntaxErrors += 1
3525 4107
3526 if reportSyntaxErrors and filesWithSyntaxErrors > 0: 4108 if reportSyntaxErrors and filesWithSyntaxErrors > 0:
3527 EricMessageBox.critical( 4109 EricMessageBox.critical(
3528 self.ui, 4110 self.ui,
3529 self.tr("Syntax errors detected"), 4111 self.tr("Syntax Errors Detected"),
3530 self.tr( 4112 self.tr(
3531 """The project contains %n file(s) with syntax errors.""", 4113 """The project contains %n file(s) with syntax errors.""",
3532 "", 4114 "",
3533 filesWithSyntaxErrors, 4115 filesWithSyntaxErrors,
3534 ), 4116 ),
3560 filesWithSyntaxErrors += 1 4142 filesWithSyntaxErrors += 1
3561 4143
3562 if reportSyntaxErrors and filesWithSyntaxErrors > 0: 4144 if reportSyntaxErrors and filesWithSyntaxErrors > 0:
3563 EricMessageBox.critical( 4145 EricMessageBox.critical(
3564 self.ui, 4146 self.ui,
3565 self.tr("Syntax errors detected"), 4147 self.tr("Syntax Errors Detected"),
3566 self.tr( 4148 self.tr(
3567 """The project contains %n file(s) with syntax errors.""", 4149 """The project contains %n file(s) with syntax errors.""",
3568 "", 4150 "",
3569 filesWithSyntaxErrors, 4151 filesWithSyntaxErrors,
3570 ), 4152 ),
3585 @return filename of the projects main script 4167 @return filename of the projects main script
3586 @rtype str 4168 @rtype str
3587 """ 4169 """
3588 if self.__pdata["MAINSCRIPT"]: 4170 if self.__pdata["MAINSCRIPT"]:
3589 if normalized: 4171 if normalized:
3590 return os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 4172 if FileSystemUtilities.isRemoteFileName(self.ppath):
4173 return self.__remotefsInterface.join(
4174 self.ppath, self.__pdata["MAINSCRIPT"]
4175 )
4176 else:
4177 return os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
3591 else: 4178 else:
3592 return self.__pdata["MAINSCRIPT"] 4179 return self.__pdata["MAINSCRIPT"]
3593 else: 4180 else:
3594 return "" 4181 return ""
3595 4182
3618 """ 4205 """
3619 if fileType not in self.getFileCategories(): 4206 if fileType not in self.getFileCategories():
3620 raise ValueError("Given file type has incorrect value.") 4207 raise ValueError("Given file type has incorrect value.")
3621 4208
3622 if normalized: 4209 if normalized:
3623 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] 4210 if FileSystemUtilities.isRemoteFileName(self.ppath):
4211 return [
4212 self.__remotefsInterface.join(self.ppath, fn)
4213 for fn in self.__pdata[fileType]
4214 ]
4215 else:
4216 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]]
3624 else: 4217 else:
3625 return self.__pdata[fileType] 4218 return self.__pdata[fileType]
3626 4219
3627 def getProjectType(self): 4220 def getProjectType(self):
3628 """ 4221 """
3713 @return tuple containing the absolute path names of the project specific 4306 @return tuple containing the absolute path names of the project specific
3714 word and exclude list 4307 word and exclude list
3715 @rtype tuple of (str, str) 4308 @rtype tuple of (str, str)
3716 """ 4309 """
3717 pwl = "" 4310 pwl = ""
3718 if self.__pdata["SPELLWORDS"]:
3719 pwl = os.path.join(self.ppath, self.__pdata["SPELLWORDS"])
3720 if not os.path.isfile(pwl):
3721 pwl = ""
3722
3723 pel = "" 4311 pel = ""
3724 if self.__pdata["SPELLEXCLUDES"]: 4312
3725 pel = os.path.join(self.ppath, self.__pdata["SPELLEXCLUDES"]) 4313 if not FileSystemUtilities.isRemoteFileName(self.ppath):
3726 if not os.path.isfile(pel): 4314 if self.__pdata["SPELLWORDS"]:
3727 pel = "" 4315 pwl = os.path.join(self.ppath, self.__pdata["SPELLWORDS"])
4316 if not os.path.isfile(pwl):
4317 pwl = ""
4318
4319 if self.__pdata["SPELLEXCLUDES"]:
4320 pel = os.path.join(self.ppath, self.__pdata["SPELLEXCLUDES"])
4321 if not os.path.isfile(pel):
4322 pel = ""
3728 4323
3729 return (pwl, pel) 4324 return (pwl, pel)
3730 4325
3731 def getDefaultSourceExtension(self): 4326 def getDefaultSourceExtension(self):
3732 """ 4327 """
3748 @return project path 4343 @return project path
3749 @rtype str 4344 @rtype str
3750 """ 4345 """
3751 return self.ppath 4346 return self.ppath
3752 4347
3753 def startswithProjectPath(self, path): 4348 def startswithProjectPath(self, checkpath):
3754 """ 4349 """
3755 Public method to check, if a path starts with the project path. 4350 Public method to check, if a path starts with the project path.
3756 4351
3757 @param path path to be checked 4352 @param checkpath path to be checked
3758 @type str 4353 @type str
3759 @return flag indicating that the path starts with the project path 4354 @return flag indicating that the path starts with the project path
3760 @rtype bool 4355 @rtype bool
3761 """ 4356 """
3762 return bool(self.ppath) and ( 4357 if FileSystemUtilities.isRemoteFileName(self.ppath):
3763 path == self.ppath 4358 return checkpath == self.ppath or checkpath.startswith(
3764 or FileSystemUtilities.normcasepath( 4359 self.ppath + self.__remotefsInterface.separator()
3765 FileSystemUtilities.toNativeSeparators(path) 4360 )
3766 ).startswith( 4361 else:
3767 FileSystemUtilities.normcasepath( 4362 return bool(self.ppath) and (
3768 FileSystemUtilities.toNativeSeparators(self.ppath + "/") 4363 checkpath == self.ppath
4364 or FileSystemUtilities.normcasepath(
4365 FileSystemUtilities.toNativeSeparators(checkpath)
4366 ).startswith(
4367 FileSystemUtilities.normcasepath(
4368 FileSystemUtilities.toNativeSeparators(self.ppath + "/")
4369 )
3769 ) 4370 )
3770 ) 4371 )
3771 )
3772 4372
3773 def getProjectFile(self): 4373 def getProjectFile(self):
3774 """ 4374 """
3775 Public method to get the path of the project file. 4375 Public method to get the path of the project file.
3776 4376
3787 4387
3788 @return name of the project 4388 @return name of the project
3789 @rtype str 4389 @rtype str
3790 """ 4390 """
3791 if self.pfile: 4391 if self.pfile:
3792 name = os.path.splitext(self.pfile)[0] 4392 return self.name
3793 return os.path.basename(name)
3794 else: 4393 else:
3795 return "" 4394 return ""
3796 4395
3797 def getProjectManagementDir(self): 4396 def getProjectManagementDir(self):
3798 """ 4397 """
3799 Public method to get the path of the management directory. 4398 Public method to get the path of the management directory.
3800 4399
3801 @return path of the management directory 4400 @return path of the management directory
3802 @rtype str 4401 @rtype str
3803 """ 4402 """
3804 return os.path.join(self.ppath, ".eric7project") 4403 if FileSystemUtilities.isRemoteFileName(self.ppath):
4404 return self.__remotefsInterface.join(self.ppath, ".eric7project")
4405 else:
4406 return os.path.join(self.ppath, ".eric7project")
3805 4407
3806 def createProjectManagementDir(self): 4408 def createProjectManagementDir(self):
3807 """ 4409 """
3808 Public method to create the project management directory. 4410 Public method to create the project management directory.
3809 4411
3810 It does nothing, if it already exists. 4412 It does nothing, if it already exists.
3811 """ 4413 """
3812 # create management directory if not present 4414 # create management directory if not present
3813 mgmtDir = self.getProjectManagementDir() 4415 mgmtDir = self.getProjectManagementDir()
3814 if not os.path.exists(mgmtDir): 4416 if FileSystemUtilities.isRemoteFileName(mgmtDir):
3815 os.makedirs(mgmtDir) 4417 self.__remotefsInterface.makedirs(mgmtDir, exist_ok=True)
4418 else:
4419 os.makedirs(mgmtDir, exist_ok=True)
3816 4420
3817 def getHash(self): 4421 def getHash(self):
3818 """ 4422 """
3819 Public method to get the project hash. 4423 Public method to get the project hash.
3820 4424
3821 @return project hash as a hex string 4425 @return project hash as a hex string
3822 @rtype str 4426 @rtype str
3823 """ 4427 """
3824 return self.__pdata["HASH"] 4428 return self.__pdata["HASH"]
3825 4429
3826 def getRelativePath(self, path): 4430 def getRelativePath(self, fullpath):
3827 """ 4431 """
3828 Public method to convert a file path to a project relative 4432 Public method to convert a file path to a project relative
3829 file path. 4433 file path.
3830 4434
3831 @param path file or directory name to convert 4435 @param fullpath file or directory name to convert
3832 @type str 4436 @type str
3833 @return project relative path or unchanged path, if path doesn't 4437 @return project relative path or unchanged path, if path doesn't
3834 belong to the project 4438 belong to the project
3835 @rtype str 4439 @rtype str
3836 """ 4440 """
3837 if path is None: 4441 if fullpath is None:
3838 return "" 4442 return ""
3839 4443
3840 try: 4444 try:
3841 return str(pathlib.Path(path).relative_to(self.ppath)) 4445 if FileSystemUtilities.isRemoteFileName(self.ppath):
4446 if self.__remotefsInterface.separator() == "\\":
4447 return str(
4448 pathlib.PureWindowsPath(fullpath).relative_to(self.ppath)
4449 )
4450 else:
4451 return str(pathlib.PurePosixPath(fullpath).relative_to(self.ppath))
4452 else:
4453 return str(pathlib.PurePath(fullpath).relative_to(self.ppath))
3842 except ValueError: 4454 except ValueError:
3843 return path 4455 return fullpath
3844 4456
3845 def getRelativeUniversalPath(self, path): 4457 def getRelativeUniversalPath(self, fullpath):
3846 """ 4458 """
3847 Public method to convert a file path to a project relative 4459 Public method to convert a file path to a project relative
3848 file path with universal separators. 4460 file path with universal separators.
3849 4461
3850 @param path file or directory name to convert 4462 @param fullpath file or directory name to convert
3851 @type str 4463 @type str
3852 @return project relative path or unchanged path, if path doesn't 4464 @return project relative path or unchanged path, if path doesn't
3853 belong to the project 4465 belong to the project
3854 @rtype str 4466 @rtype str
3855 """ 4467 """
3856 return FileSystemUtilities.fromNativeSeparators(self.getRelativePath(path)) 4468 if FileSystemUtilities.isRemoteFileName(self.ppath):
4469 return self.__remotefsInterface.fromNativeSeparators(
4470 self.getRelativePath(fullpath)
4471 )
4472 else:
4473 return FileSystemUtilities.fromNativeSeparators(
4474 self.getRelativePath(fullpath)
4475 )
3857 4476
3858 def getAbsolutePath(self, fn): 4477 def getAbsolutePath(self, fn):
3859 """ 4478 """
3860 Public method to convert a project relative file path to an absolute 4479 Public method to convert a project relative file path to an absolute
3861 file path. 4480 file path.
3863 @param fn file or directory name to convert 4482 @param fn file or directory name to convert
3864 @type str 4483 @type str
3865 @return absolute path 4484 @return absolute path
3866 @rtype str 4485 @rtype str
3867 """ 4486 """
3868 if not os.path.isabs(fn): 4487 if not fn.startswith(self.ppath):
3869 fn = os.path.join(self.ppath, fn) 4488 if FileSystemUtilities.isRemoteFileName(self.ppath):
4489 fn = self.__remotefsInterface.join(self.ppath, fn)
4490 else:
4491 fn = os.path.join(self.ppath, fn)
3870 return fn 4492 return fn
3871 4493
3872 def getAbsoluteUniversalPath(self, fn): 4494 def getAbsoluteUniversalPath(self, fn):
3873 """ 4495 """
3874 Public method to convert a project relative file path with universal 4496 Public method to convert a project relative file path with universal
3877 @param fn file or directory name to convert 4499 @param fn file or directory name to convert
3878 @type str 4500 @type str
3879 @return absolute path 4501 @return absolute path
3880 @rtype str 4502 @rtype str
3881 """ 4503 """
3882 if not os.path.isabs(fn): 4504 if not fn.startswith(self.ppath):
3883 fn = os.path.join(self.ppath, FileSystemUtilities.toNativeSeparators(fn)) 4505 if FileSystemUtilities.isRemoteFileName(self.ppath):
4506 fn = self.__remotefsInterface.join(
4507 self.ppath, self.__remotefsInterface.fromNativeSeparators(fn)
4508 )
4509 else:
4510 fn = os.path.join(
4511 self.ppath, FileSystemUtilities.fromNativeSeparators(fn)
4512 )
3884 return fn 4513 return fn
3885 4514
3886 def getEolString(self): 4515 def getEolString(self):
3887 """ 4516 """
3888 Public method to get the EOL-string to be used by the project. 4517 Public method to get the EOL-string to be used by the project.
3960 @return name of the project's virtual environment 4589 @return name of the project's virtual environment
3961 @rtype str 4590 @rtype str
3962 """ 4591 """
3963 venvName = ( 4592 venvName = (
3964 self.__venvConfiguration["name"] 4593 self.__venvConfiguration["name"]
3965 if self.__pdata["EMBEDDED_VENV"] and bool(self.__venvConfiguration["name"]) 4594 if (
4595 self.__pdata["EMBEDDED_VENV"]
4596 and not FileSystemUtilities.isRemoteFileName(self.ppath)
4597 and bool(self.__venvConfiguration["name"])
4598 )
3966 else self.getDebugProperty("VIRTUALENV") 4599 else self.getDebugProperty("VIRTUALENV")
3967 ) 4600 )
3968 if ( 4601 if (
3969 not venvName 4602 not venvName
3970 and resolveDebugger 4603 and resolveDebugger
3979 Public method to get the path name of the embedded virtual environment. 4612 Public method to get the path name of the embedded virtual environment.
3980 4613
3981 @return path name of the embedded virtual environment 4614 @return path name of the embedded virtual environment
3982 @rtype str 4615 @rtype str
3983 """ 4616 """
3984 if self.__pdata["EMBEDDED_VENV"]: 4617 if self.__pdata["EMBEDDED_VENV"] and not FileSystemUtilities.isRemoteFileName(
4618 self.ppath
4619 ):
3985 return self.__findEmbeddedEnvironment() 4620 return self.__findEmbeddedEnvironment()
3986 else: 4621 else:
3987 return "" 4622 return ""
3988 4623
3989 def getProjectInterpreter(self, resolveGlobal=True): 4624 def getProjectInterpreter(self, resolveGlobal=True):
3997 @return path of the project's interpreter 4632 @return path of the project's interpreter
3998 @rtype str 4633 @rtype str
3999 """ 4634 """
4000 interpreter = ( 4635 interpreter = (
4001 self.__venvConfiguration["interpreter"] 4636 self.__venvConfiguration["interpreter"]
4002 if self.__pdata["EMBEDDED_VENV"] 4637 if (
4638 self.__pdata["EMBEDDED_VENV"]
4639 and not FileSystemUtilities.isRemoteFileName(self.ppath)
4640 )
4003 else "" 4641 else ""
4004 ) 4642 )
4005 if not interpreter: 4643 if not interpreter:
4006 venvName = self.getProjectVenv() 4644 venvName = self.getProjectVenv()
4007 if venvName: 4645 if venvName:
4068 @param fn filename to be checked 4706 @param fn filename to be checked
4069 @type str 4707 @type str
4070 @return flag indicating membership 4708 @return flag indicating membership
4071 @rtype bool 4709 @rtype bool
4072 """ 4710 """
4073 newfn = os.path.abspath(fn) 4711 newfn = (
4712 self.__remotefsInterface.abspath(fn)
4713 if FileSystemUtilities.isRemoteFileName(self.ppath)
4714 else os.path.abspath(fn)
4715 )
4074 newfn = self.getRelativePath(newfn) 4716 newfn = self.getRelativePath(newfn)
4075 return any( 4717 return any(
4076 newfn in self.__pdata[category] for category in self.getFileCategories() 4718 newfn in self.__pdata[category] for category in self.getFileCategories()
4077 ) 4719 )
4078 4720
4101 @param group group to check 4743 @param group group to check
4102 @type str 4744 @type str
4103 @return flag indicating membership 4745 @return flag indicating membership
4104 @rtype bool 4746 @rtype bool
4105 """ 4747 """
4106 newfn = os.path.abspath(fn) 4748 newfn = (
4749 self.__remotefsInterface.abspath(fn)
4750 if FileSystemUtilities.isRemoteFileName(fn)
4751 else os.path.abspath(fn)
4752 )
4107 newfn = self.getRelativePath(newfn) 4753 newfn = self.getRelativePath(newfn)
4108 if newfn in self.__pdata[group] or ( 4754 if newfn in self.__pdata[group] or (
4109 group == "OTHERS" 4755 group == "OTHERS"
4110 and any(newfn.startswith(entry) for entry in self.__pdata[group]) 4756 and any(newfn.startswith(entry) for entry in self.__pdata[group])
4111 ): 4757 ):
4112 return True 4758 return True
4113 4759
4114 if OSUtilities.isWindowsPlatform(): 4760 if (
4761 OSUtilities.isWindowsPlatform()
4762 or self.__remotefsInterface.separator() == "\\"
4763 ):
4115 # try the above case-insensitive 4764 # try the above case-insensitive
4116 newfn = newfn.lower() 4765 newfn = newfn.lower()
4117 if any(entry.lower() == newfn for entry in self.__pdata[group]): 4766 if any(entry.lower() == newfn for entry in self.__pdata[group]):
4118 return True 4767 return True
4119 4768
4187 self.tr("""<b>Open...</b><p>This opens an existing project.</p>""") 4836 self.tr("""<b>Open...</b><p>This opens an existing project.</p>""")
4188 ) 4837 )
4189 act.triggered.connect(self.openProject) 4838 act.triggered.connect(self.openProject)
4190 self.actions.append(act) 4839 self.actions.append(act)
4191 4840
4841 self.openRemoteAct = EricAction(
4842 self.tr("Open remote project"),
4843 EricPixmapCache.getIcon("projectOpen-remote"),
4844 self.tr("Open (Remote)..."),
4845 0,
4846 0,
4847 self.actGrp1,
4848 "project_open_remote",
4849 )
4850 self.openRemoteAct.setStatusTip(self.tr("Open an existing remote project"))
4851 self.openRemoteAct.setWhatsThis(
4852 self.tr(
4853 "<b>Open (Remote)...</b><p>This opens an existing remote project.</p>"
4854 )
4855 )
4856 self.openRemoteAct.triggered.connect(self.__openRemoteProject)
4857 self.actions.append(self.openRemoteAct)
4858 self.openRemoteAct.setEnabled(False) # server is not connected initially
4859
4192 self.reloadAct = EricAction( 4860 self.reloadAct = EricAction(
4193 self.tr("Reload project"), 4861 self.tr("Reload project"),
4194 EricPixmapCache.getIcon("projectReload"), 4862 EricPixmapCache.getIcon("projectReload"),
4195 self.tr("Re&load"), 4863 self.tr("Re&load"),
4196 0, 4864 0,
4253 """<p>This saves the current project to a new file.</p>""" 4921 """<p>This saves the current project to a new file.</p>"""
4254 ) 4922 )
4255 ) 4923 )
4256 self.saveasAct.triggered.connect(self.saveProjectAs) 4924 self.saveasAct.triggered.connect(self.saveProjectAs)
4257 self.actions.append(self.saveasAct) 4925 self.actions.append(self.saveasAct)
4926
4927 self.saveasRemoteAct = EricAction(
4928 self.tr("Save project as (Remote)"),
4929 EricPixmapCache.getIcon("projectSaveAs-remote"),
4930 self.tr("Save as (Remote)..."),
4931 0,
4932 0,
4933 self,
4934 "project_save_as_remote",
4935 )
4936 self.saveasRemoteAct.setStatusTip(
4937 self.tr("Save the current project to a new remote file")
4938 )
4939 self.saveasRemoteAct.setWhatsThis(
4940 self.tr(
4941 """<b>Save as (Remote)</b>"""
4942 """<p>This saves the current project to a new remote file.</p>"""
4943 )
4944 )
4945 self.saveasRemoteAct.triggered.connect(self.__saveRemoteProjectAs)
4946 self.actions.append(self.saveasRemoteAct)
4947 self.saveasRemoteAct.setEnabled(False) # server is not connected initially
4258 4948
4259 ################################################################### 4949 ###################################################################
4260 ## Project management actions 4950 ## Project management actions
4261 ################################################################### 4951 ###################################################################
4262 4952
5246 @return tuple of generated menus 5936 @return tuple of generated menus
5247 @rtype tuple of (QMenu, QMenu) 5937 @rtype tuple of (QMenu, QMenu)
5248 """ 5938 """
5249 menu = QMenu(self.tr("&Project"), self.parent()) 5939 menu = QMenu(self.tr("&Project"), self.parent())
5250 self.recentMenu = QMenu(self.tr("Open &Recent Projects"), menu) 5940 self.recentMenu = QMenu(self.tr("Open &Recent Projects"), menu)
5941 self.recentMenu.setIcon(EricPixmapCache.getIcon("projectOpenRecent"))
5251 self.sessionMenu = QMenu(self.tr("Session"), menu) 5942 self.sessionMenu = QMenu(self.tr("Session"), menu)
5252 self.debuggerMenu = QMenu(self.tr("Debugger"), menu) 5943 self.debuggerMenu = QMenu(self.tr("Debugger"), menu)
5253 self.environmentMenu = QMenu(self.tr("Embedded Environment"), menu) 5944 self.environmentMenu = QMenu(self.tr("Embedded Environment"), menu)
5254 5945
5255 toolsMenu = QMenu(self.tr("Project-T&ools"), self.parent()) 5946 toolsMenu = QMenu(self.tr("Project-T&ools"), self.parent())
5361 menu.addSeparator() 6052 menu.addSeparator()
5362 menu.addAction(self.closeAct) 6053 menu.addAction(self.closeAct)
5363 menu.addSeparator() 6054 menu.addSeparator()
5364 menu.addAction(self.saveAct) 6055 menu.addAction(self.saveAct)
5365 menu.addAction(self.saveasAct) 6056 menu.addAction(self.saveasAct)
6057 menu.addAction(self.saveasRemoteAct)
5366 menu.addSeparator() 6058 menu.addSeparator()
5367 menu.addActions(self.actGrp2.actions()) 6059 menu.addActions(self.actGrp2.actions())
5368 menu.addSeparator() 6060 menu.addSeparator()
5369 menu.addAction(self.propsAct) 6061 menu.addAction(self.propsAct)
5370 menu.addAction(self.userPropsAct) 6062 menu.addAction(self.userPropsAct)
5377 self.menuSessionAct = menu.addMenu(self.sessionMenu) 6069 self.menuSessionAct = menu.addMenu(self.sessionMenu)
5378 6070
5379 # build the project tools menu 6071 # build the project tools menu
5380 toolsMenu.setTearOffEnabled(True) 6072 toolsMenu.setTearOffEnabled(True)
5381 toolsMenu.addSeparator() 6073 toolsMenu.addSeparator()
5382 toolsMenu.addMenu(self.vcsMenu) 6074 self.menuVcsAct = toolsMenu.addMenu(self.vcsMenu)
5383 toolsMenu.addSeparator() 6075 toolsMenu.addSeparator()
5384 self.menuCheckAct = toolsMenu.addMenu(self.checksMenu) 6076 self.menuCheckAct = toolsMenu.addMenu(self.checksMenu)
5385 toolsMenu.addSeparator() 6077 toolsMenu.addSeparator()
5386 self.menuFormattingAct = toolsMenu.addMenu(self.formattingMenu) 6078 self.menuFormattingAct = toolsMenu.addMenu(self.formattingMenu)
5387 toolsMenu.addSeparator() 6079 toolsMenu.addSeparator()
5433 tb.addActions(self.actGrp1.actions()) 6125 tb.addActions(self.actGrp1.actions())
5434 tb.addAction(self.closeAct) 6126 tb.addAction(self.closeAct)
5435 tb.addSeparator() 6127 tb.addSeparator()
5436 tb.addAction(self.saveAct) 6128 tb.addAction(self.saveAct)
5437 tb.addAction(self.saveasAct) 6129 tb.addAction(self.saveasAct)
6130 tb.addAction(self.saveasRemoteAct)
5438 6131
5439 toolbarManager.addToolBar(tb, tb.windowTitle()) 6132 toolbarManager.addToolBar(tb, tb.windowTitle())
5440 toolbarManager.addAction(self.addFilesAct, tb.windowTitle()) 6133 toolbarManager.addAction(self.addFilesAct, tb.windowTitle())
5441 toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle()) 6134 toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle())
5442 toolbarManager.addAction(self.addLanguageAct, tb.windowTitle()) 6135 toolbarManager.addAction(self.addLanguageAct, tb.windowTitle())
5450 def __showMenu(self): 6143 def __showMenu(self):
5451 """ 6144 """
5452 Private method to set up the project menu. 6145 Private method to set up the project menu.
5453 """ 6146 """
5454 self.menuRecentAct.setEnabled(len(self.recent) > 0) 6147 self.menuRecentAct.setEnabled(len(self.recent) > 0)
5455 self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) 6148 self.menuEnvironmentAct.setEnabled(
6149 self.__pdata["EMBEDDED_VENV"]
6150 and not FileSystemUtilities.isRemoteFileName(self.ppath)
6151 )
5456 6152
5457 self.showMenu.emit("Main", self.__menus["Main"]) 6153 self.showMenu.emit("Main", self.__menus["Main"])
5458 6154
5459 def __syncRecent(self): 6155 def __syncRecent(self):
5460 """ 6156 """
5461 Private method to synchronize the list of recently opened projects 6157 Private method to synchronize the list of recently opened projects
5462 with the central store. 6158 with the central store.
5463 """ 6159 """
5464 for recent in self.recent[:]: 6160 for recent in self.recent[:]:
5465 if FileSystemUtilities.samepath(self.pfile, recent): 6161 if (
6162 FileSystemUtilities.isRemoteFileName(recent) and recent == self.pfile
6163 ) or FileSystemUtilities.samepath(self.pfile, recent):
5466 self.recent.remove(recent) 6164 self.recent.remove(recent)
5467 self.recent.insert(0, self.pfile) 6165 self.recent.insert(0, self.pfile)
5468 maxRecent = Preferences.getProject("RecentNumber") 6166 maxRecent = Preferences.getProject("RecentNumber")
5469 if len(self.recent) > maxRecent: 6167 if len(self.recent) > maxRecent:
5470 self.recent = self.recent[:maxRecent] 6168 self.recent = self.recent[:maxRecent]
5480 6178
5481 for idx, rp in enumerate(self.recent, start=1): 6179 for idx, rp in enumerate(self.recent, start=1):
5482 formatStr = "&{0:d}. {1}" if idx < 10 else "{0:d}. {1}" 6180 formatStr = "&{0:d}. {1}" if idx < 10 else "{0:d}. {1}"
5483 act = self.recentMenu.addAction( 6181 act = self.recentMenu.addAction(
5484 formatStr.format( 6182 formatStr.format(
5485 idx, FileSystemUtilities.compactPath(rp, self.ui.maxMenuFilePathLen) 6183 idx,
6184 (
6185 self.__remotefsInterface.compactPath(
6186 rp, self.ui.maxMenuFilePathLen
6187 )
6188 if FileSystemUtilities.isRemoteFileName(rp)
6189 else FileSystemUtilities.compactPath(
6190 rp, self.ui.maxMenuFilePathLen
6191 )
6192 ),
5486 ) 6193 )
5487 ) 6194 )
5488 act.setData(rp) 6195 act.setData(rp)
5489 act.setEnabled(pathlib.Path(rp).exists()) 6196 if FileSystemUtilities.isRemoteFileName(rp):
6197 act.setEnabled(
6198 self.__remoteServer.isServerConnected
6199 and self.__remotefsInterface.exists(rp)
6200 )
6201 else:
6202 act.setEnabled(pathlib.Path(rp).exists())
5490 6203
5491 self.recentMenu.addSeparator() 6204 self.recentMenu.addSeparator()
5492 self.recentMenu.addAction(self.tr("&Clear"), self.clearRecent) 6205 self.recentMenu.addAction(self.tr("&Clear"), self.clearRecent)
5493 6206
5494 def __openRecent(self, act): 6207 def __openRecent(self, act):
5535 if self.__findProjectFileDialog is None: 6248 if self.__findProjectFileDialog is None:
5536 self.__findProjectFileDialog = QuickFindFileDialog(self) 6249 self.__findProjectFileDialog = QuickFindFileDialog(self)
5537 self.__findProjectFileDialog.sourceFile.connect(self.sourceFile) 6250 self.__findProjectFileDialog.sourceFile.connect(self.sourceFile)
5538 self.__findProjectFileDialog.designerFile.connect(self.designerFile) 6251 self.__findProjectFileDialog.designerFile.connect(self.designerFile)
5539 self.__findProjectFileDialog.linguistFile.connect(self.linguistFile) 6252 self.__findProjectFileDialog.linguistFile.connect(self.linguistFile)
5540 self.__findProjectFileDialog.show() 6253 self.__findProjectFileDialog.show(
6254 FileSystemUtilities.isRemoteFileName(self.ppath)
6255 )
5541 self.__findProjectFileDialog.raise_() 6256 self.__findProjectFileDialog.raise_()
5542 self.__findProjectFileDialog.activateWindow() 6257 self.__findProjectFileDialog.activateWindow()
5543 6258
5544 def __doSearchNewFiles(self, AI=True, onUserDemand=False): 6259 def __doSearchNewFiles(self, AI=True, onUserDemand=False):
5545 """ 6260 """
5560 from .AddFoundFilesDialog import AddFoundFilesDialog 6275 from .AddFoundFilesDialog import AddFoundFilesDialog
5561 6276
5562 autoInclude = Preferences.getProject("AutoIncludeNewFiles") 6277 autoInclude = Preferences.getProject("AutoIncludeNewFiles")
5563 recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") 6278 recursiveSearch = Preferences.getProject("SearchNewFilesRecursively")
5564 newFiles = [] 6279 newFiles = []
6280
6281 isRemote = FileSystemUtilities.isRemoteFileName(self.ppath)
5565 6282
5566 ignore_patterns = [ 6283 ignore_patterns = [
5567 pattern 6284 pattern
5568 for pattern, filetype in self.__pdata["FILETYPES"].items() 6285 for pattern, filetype in self.__pdata["FILETYPES"].items()
5569 if filetype == "__IGNORE__" 6286 if filetype == "__IGNORE__"
5575 fnmatch.fnmatch(directory, ignore_pattern) 6292 fnmatch.fnmatch(directory, ignore_pattern)
5576 for ignore_pattern in ignore_patterns 6293 for ignore_pattern in ignore_patterns
5577 ): 6294 ):
5578 continue 6295 continue
5579 6296
5580 curpath = os.path.join(self.ppath, directory) 6297 curpath = (
6298 self.__remotefsInterface.join(self.ppath, directory)
6299 if isRemote
6300 else os.path.join(self.ppath, directory)
6301 )
5581 try: 6302 try:
5582 newSources = os.listdir(curpath) 6303 newSources = (
6304 [e["name"] for e in self.__remotefsInterface.listdir(curpath)[2]]
6305 if isRemote
6306 else os.listdir(curpath)
6307 )
5583 except OSError: 6308 except OSError:
5584 newSources = [] 6309 newSources = []
5585 pattern = ( 6310 pattern = (
5586 self.__pdata["TRANSLATIONPATTERN"].replace("%language%", "*") 6311 self.__pdata["TRANSLATIONPATTERN"].replace("%language%", "*")
5587 if self.__pdata["TRANSLATIONPATTERN"] 6312 if self.__pdata["TRANSLATIONPATTERN"]
5594 continue 6319 continue
5595 6320
5596 # set fn to project relative name 6321 # set fn to project relative name
5597 # then reset ns to fully qualified name for insertion, 6322 # then reset ns to fully qualified name for insertion,
5598 # possibly. 6323 # possibly.
5599 fn = os.path.join(directory, ns) if directory else ns 6324 if isRemote:
5600 ns = os.path.abspath(os.path.join(curpath, ns)) 6325 fn = (
6326 self.__remotefsInterface.join(directory, ns)
6327 if directory
6328 else ns
6329 )
6330 ns = self.__remotefsInterface.abspath(
6331 self.__remotefsInterface.join(curpath, ns)
6332 )
6333
6334 isdir_ns = self.__remotefsInterface.isdir(ns)
6335 else:
6336 fn = os.path.join(directory, ns) if directory else ns
6337 ns = os.path.abspath(os.path.join(curpath, ns))
6338 isdir_ns = os.path.isdir(ns)
5601 6339
5602 # do not bother with dirs here... 6340 # do not bother with dirs here...
5603 if os.path.isdir(ns): 6341 if isdir_ns:
5604 if recursiveSearch: 6342 if recursiveSearch:
5605 d = self.getRelativePath(ns) 6343 d = self.getRelativePath(ns)
5606 if d not in dirs: 6344 if d not in dirs:
5607 dirs.append(d) # noqa: M569 6345 dirs.append(d) # noqa: M569
5608 continue 6346 continue
5609 6347
5610 filetype = "" 6348 filetype = ""
5611 bfn = os.path.basename(fn) 6349 bfn = (
6350 self.__remotefsInterface.basename(fn)
6351 if isRemote
6352 else os.path.basename(fn)
6353 )
5612 6354
5613 # check against ignore patterns first (see issue 553) 6355 # check against ignore patterns first (see issue 553)
5614 if any( 6356 if any(
5615 fnmatch.fnmatch(bfn, ignore_pattern) 6357 fnmatch.fnmatch(bfn, ignore_pattern)
5616 for ignore_pattern in ignore_patterns 6358 for ignore_pattern in ignore_patterns
5624 6366
5625 if ( 6367 if (
5626 filetype in self.getFileCategories() 6368 filetype in self.getFileCategories()
5627 and ( 6369 and (
5628 fn not in self.__pdata[filetype] 6370 fn not in self.__pdata[filetype]
5629 and ( 6371 or (
5630 filetype == "OTHERS" 6372 filetype == "OTHERS"
6373 and fn not in self.__pdata[filetype]
5631 and os.path.dirname(fn) not in self.__pdata["OTHERS"] 6374 and os.path.dirname(fn) not in self.__pdata["OTHERS"]
5632 ) 6375 )
5633 ) 6376 )
5634 and ( 6377 and (
5635 filetype != "TRANSLATIONS" 6378 filetype != "TRANSLATIONS"
5767 6510
5768 vcs = None 6511 vcs = None
5769 forProject = True 6512 forProject = True
5770 override = False 6513 override = False
5771 6514
6515 if FileSystemUtilities.isRemoteFileName(self.ppath):
6516 return None
6517
5772 if vcsSystem is None: 6518 if vcsSystem is None:
5773 if self.__pdata["VCS"] and self.__pdata["VCS"] != "None": 6519 if self.__pdata["VCS"] and self.__pdata["VCS"] != "None":
5774 vcsSystem = self.__pdata["VCS"] 6520 vcsSystem = self.__pdata["VCS"]
5775 else: 6521 else:
5776 forProject = False 6522 forProject = False
5943 """ 6689 """
5944 Private slot used to calculate some code metrics for the project files. 6690 Private slot used to calculate some code metrics for the project files.
5945 """ 6691 """
5946 from eric7.DataViews.CodeMetricsDialog import CodeMetricsDialog 6692 from eric7.DataViews.CodeMetricsDialog import CodeMetricsDialog
5947 6693
5948 files = [ 6694 files = (
5949 os.path.join(self.ppath, file) 6695 [
5950 for file in self.__pdata["SOURCES"] 6696 self.__remotefsInterface.join(self.ppath, file)
5951 if file.endswith(".py") 6697 for file in self.__pdata["SOURCES"]
5952 ] 6698 if file.endswith(".py")
6699 ]
6700 if FileSystemUtilities.isRemoteFileName(self.ppath)
6701 else [
6702 os.path.join(self.ppath, file)
6703 for file in self.__pdata["SOURCES"]
6704 if file.endswith(".py")
6705 ]
6706 )
5953 self.codemetrics = CodeMetricsDialog() 6707 self.codemetrics = CodeMetricsDialog()
5954 self.codemetrics.show() 6708 self.codemetrics.show()
5955 self.codemetrics.prepare(files) 6709 self.codemetrics.prepare(files)
5956 6710
5957 def __showCodeCoverage(self): 6711 def __showCodeCoverage(self):
5989 else: 6743 else:
5990 fn = files[0] 6744 fn = files[0]
5991 else: 6745 else:
5992 return 6746 return
5993 6747
5994 files = [ 6748 files = (
5995 os.path.join(self.ppath, file) 6749 [
5996 for file in self.__pdata["SOURCES"] 6750 self.__remotefsInterface.join(self.ppath, file)
5997 if os.path.splitext(file)[1].startswith(".py") 6751 for file in self.__pdata["SOURCES"]
5998 ] 6752 if self.__remotefsInterface.splitext(file)[1].startswith(".py")
6753 ]
6754 if FileSystemUtilities.isRemoteFileName(self.ppath)
6755 else [
6756 os.path.join(self.ppath, file)
6757 for file in self.__pdata["SOURCES"]
6758 if os.path.splitext(file)[1].startswith(".py")
6759 ]
6760 )
5999 self.codecoverage = PyCoverageDialog() 6761 self.codecoverage = PyCoverageDialog()
6000 self.codecoverage.show() 6762 self.codecoverage.show()
6001 self.codecoverage.start(fn, files) 6763 self.codecoverage.start(fn, files)
6002 6764
6003 def __showProfileData(self): 6765 def __showProfileData(self):
6408 if name == self.__pdata["MAINSCRIPT"]: 7170 if name == self.__pdata["MAINSCRIPT"]:
6409 version = self.__pluginExtractVersion( 7171 version = self.__pluginExtractVersion(
6410 os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) 7172 os.path.join(self.ppath, self.__pdata["MAINSCRIPT"])
6411 ) 7173 )
6412 if archiveVersion and ( 7174 if archiveVersion and (
6413 Globals.versionToTuple(version) 7175 EricUtilities.versionToTuple(version)
6414 < Globals.versionToTuple(archiveVersion) 7176 < EricUtilities.versionToTuple(archiveVersion)
6415 ): 7177 ):
6416 version = archiveVersion 7178 version = archiveVersion
6417 except OSError as why: 7179 except OSError as why:
6418 EricMessageBox.critical( 7180 EricMessageBox.critical(
6419 self.ui, 7181 self.ui,
6604 Public method to test, if make is enabled for the project. 7366 Public method to test, if make is enabled for the project.
6605 7367
6606 @return flag indicating enabled make support 7368 @return flag indicating enabled make support
6607 @rtype bool 7369 @rtype bool
6608 """ 7370 """
6609 return self.__pdata["MAKEPARAMS"]["MakeEnabled"] 7371 return self.__pdata["MAKEPARAMS"][
7372 "MakeEnabled"
7373 ] and not FileSystemUtilities.isRemoteFileName(self.ppath)
6610 7374
6611 @pyqtSlot() 7375 @pyqtSlot()
6612 def __autoExecuteMake(self): 7376 def __autoExecuteMake(self):
6613 """ 7377 """
6614 Private slot to execute a project specific make run (auto-run) 7378 Private slot to execute a project specific make run (auto-run)
6615 (execute or question). 7379 (execute or question).
6616 """ 7380 """
6617 if Preferences.getProject("AutoExecuteMake"): 7381 if Preferences.getProject(
7382 "AutoExecuteMake"
7383 ) and not FileSystemUtilities.isRemoteFileName(self.ppath):
6618 self.__executeMake(questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"]) 7384 self.__executeMake(questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"])
6619 7385
6620 @pyqtSlot() 7386 @pyqtSlot()
6621 def __executeMake(self, questionOnly=False): 7387 def __executeMake(self, questionOnly=False):
6622 """ 7388 """
6623 Private method to execute a project specific make run. 7389 Private method to execute a project specific make run.
6624 7390
6625 @param questionOnly flag indicating to ask make for changes only 7391 @param questionOnly flag indicating to ask make for changes only
6626 @type bool 7392 @type bool
6627 """ 7393 """
7394 if FileSystemUtilities.isRemoteFileName(self.ppath):
7395 EricMessageBox.critical(
7396 self.ui,
7397 self.tr("Execute Make"),
7398 self.tr("'Make' is not supported for remote projects. Aborting..."),
7399 )
7400 return
7401
6628 if ( 7402 if (
6629 not self.__pdata["MAKEPARAMS"]["MakeEnabled"] 7403 not self.__pdata["MAKEPARAMS"]["MakeEnabled"]
6630 or self.__makeProcess is not None 7404 or self.__makeProcess is not None
6631 ): 7405 ):
6632 return 7406 return
6848 7622
6849 def __showContextMenuOthers(self): 7623 def __showContextMenuOthers(self):
6850 """ 7624 """
6851 Private slot called before the 'Other Tools' menu is shown. 7625 Private slot called before the 'Other Tools' menu is shown.
6852 """ 7626 """
7627 self.createSBOMAct.setEnabled(
7628 not FileSystemUtilities.isRemoteFileName(self.ppath)
7629 )
7630
6853 self.showMenu.emit("OtherTools", self.othersMenu) 7631 self.showMenu.emit("OtherTools", self.othersMenu)
6854 7632
6855 @pyqtSlot() 7633 @pyqtSlot()
6856 def __createSBOMFile(self): 7634 def __createSBOMFile(self):
6857 """ 7635 """
6988 7766
6989 @return path of the embedded virtual environment (empty if not found) 7767 @return path of the embedded virtual environment (empty if not found)
6990 @rtype str 7768 @rtype str
6991 """ 7769 """
6992 ppath = self.getProjectPath() 7770 ppath = self.getProjectPath()
6993 if ppath and os.path.exists(ppath): 7771 if FileSystemUtilities.isRemoteFileName(ppath):
6994 with os.scandir(self.getProjectPath()) as ppathDirEntriesIterator: 7772 # check for some common names
6995 for dirEntry in ppathDirEntriesIterator:
6996 # potential venv directory; check for 'pyvenv.cfg'
6997 if dirEntry.is_dir() and os.path.exists(
6998 os.path.join(dirEntry.path, "pyvenv.cfg")
6999 ):
7000 return dirEntry.path
7001
7002 # check for some common names in case 'pyvenv.cfg' is missing
7003 for venvPathName in (".venv", "venv", ".env", "env"): 7773 for venvPathName in (".venv", "venv", ".env", "env"):
7004 venvPath = os.path.join(self.getProjectPath(), venvPathName) 7774 venvPath = self.__remotefsInterface.join(ppath, venvPathName)
7005 if os.path.isdir(venvPath): 7775 if self.__remotefsInterface.isdir(venvPath):
7006 return venvPath 7776 return venvPath
7777 else:
7778 if ppath and os.path.exists(ppath):
7779 with os.scandir(self.getProjectPath()) as ppathDirEntriesIterator:
7780 for dirEntry in ppathDirEntriesIterator:
7781 # potential venv directory; check for 'pyvenv.cfg'
7782 if dirEntry.is_dir() and os.path.exists(
7783 os.path.join(dirEntry.path, "pyvenv.cfg")
7784 ):
7785 return dirEntry.path
7786
7787 # check for some common names in case 'pyvenv.cfg' is missing
7788 for venvPathName in (".venv", "venv", ".env", "env"):
7789 venvPath = os.path.join(ppath, venvPathName)
7790 if os.path.isdir(venvPath):
7791 return venvPath
7007 7792
7008 return "" 7793 return ""
7009 7794
7010 def __setEmbeddedEnvironmentProjectConfig(self, value): 7795 def __setEmbeddedEnvironmentProjectConfig(self, value):
7011 """ 7796 """
7207 with os.scandir(directory) as dirEntriesIterator: 7992 with os.scandir(directory) as dirEntriesIterator:
7208 for dirEntry in dirEntriesIterator: 7993 for dirEntry in dirEntriesIterator:
7209 if dirEntry.is_dir(): 7994 if dirEntry.is_dir():
7210 self.__clearByteCodeCaches(dirEntry.path) 7995 self.__clearByteCodeCaches(dirEntry.path)
7211 7996
7997 #############################################################################
7998 ## Below are methods implementing the support for 'eric-ide server projects
7999 #############################################################################
8000
8001 @pyqtSlot(bool)
8002 def remoteConnectionChanged(self, connected):
8003 """
8004 Public slot to handle a change of the 'eric-ide' server connection state.
8005
8006 @param connected flag indicating the connection state
8007 @type bool
8008 """
8009 self.openRemoteAct.setEnabled(connected)
8010 self.saveasRemoteAct.setEnabled(
8011 connected
8012 and self.opened
8013 and FileSystemUtilities.isRemoteFileName(self.pfile)
8014 )
8015 if not connected and FileSystemUtilities.isRemoteFileName(self.ppath):
8016 self.closeProject(noSave=True)
8017
8018 @pyqtSlot()
8019 def __openRemoteProject(self):
8020 """
8021 Private slot to open a project of an 'eric-ide' server.
8022 """
8023 fn = EricServerFileDialog.getOpenFileName(
8024 self.parent(),
8025 self.tr("Open Remote Project"),
8026 "",
8027 self.tr("Project Files (*.epj)"),
8028 )
8029 if fn:
8030 self.openProject(fn=fn)
8031
8032 @pyqtSlot()
8033 def __saveRemoteProjectAs(self):
8034 """
8035 Private slot to save the current remote project to different remote file.
8036 """
8037 defaultFilter = self.tr("Project Files (*.epj)")
8038 defaultPath = self.ppath if self.ppath else ""
8039 fn, selectedFilter = EricServerFileDialog.getSaveFileNameAndFilter(
8040 self.parent(),
8041 self.tr("Save Remote Project"),
8042 defaultPath,
8043 self.tr("Project Files (*.epj)"),
8044 defaultFilter,
8045 )
8046
8047 if fn:
8048 fname, ext = self.__remotefsInterface.splitext(fn)
8049 if not ext:
8050 ex = selectedFilter.split("(*")[1].split(")")[0]
8051 if ex:
8052 fn = f"{fname}{ex}"
8053 if self.__remotefsInterface.exists(fn):
8054 res = EricMessageBox.yesNo(
8055 self.ui,
8056 self.tr("Save Remote Project"),
8057 self.tr(
8058 """<p>The file <b>{0}</b> already exists."""
8059 """ Overwrite it?</p>"""
8060 ).format(fn),
8061 icon=EricMessageBox.Warning,
8062 )
8063 if not res:
8064 return
8065
8066 ok = self.__writeProject(fn)
8067
8068 if ok:
8069 # create management directory if not present
8070 self.createProjectManagementDir()
8071
8072 # now save the tasks
8073 self.writeTasks()
8074
8075 self.sessActGrp.setEnabled(ok)
8076 self.menuSessionAct.setEnabled(ok)
8077 self.projectClosedHooks.emit()
8078 self.projectClosed.emit(False)
8079 self.projectOpenedHooks.emit()
8080 self.projectOpened.emit()
8081
7212 8082
7213 # 8083 #
7214 # eflag: noqa = M601 8084 # eflag: noqa = M601

eric ide

mercurial