diff -r b6f5e27daeb5 -r bb0149571d94 src/eric7/Project/Project.py --- a/src/eric7/Project/Project.py Fri Feb 23 16:52:01 2024 +0100 +++ b/src/eric7/Project/Project.py Mon Feb 26 10:41:10 2024 +0100 @@ -513,7 +513,6 @@ self.opened = False self.subdirs = [] # record the project dir as a relative path (i.e. empty path) - self.otherssubdirs = [] self.vcs = None self.vcsRequested = False self.dbgVirtualEnv = "" @@ -981,7 +980,13 @@ rp = Preferences.Prefs.rsettings.value(recentNameProject) if rp is not None: for f in rp: - if pathlib.Path(f).exists(): + if ( + FileSystemUtilities.isRemoteFileName(f) + and ( + not self.__remoteServer.isServerConnected() + or self.__remotefsInterface.exists(f) + ) + ) or pathlib.Path(f).exists(): self.recent.append(f) def __saveRecent(self): @@ -1193,8 +1198,6 @@ if FileSystemUtilities.isRemoteFileName(fn) else os.path.dirname(fn) ) - if dn and dn not in self.otherssubdirs: - self.otherssubdirs.append(dn) return res @@ -2019,8 +2022,6 @@ dirty = True else: updateModel and self.repopulateItem(newfn) - if newdir not in self.otherssubdirs: - self.otherssubdirs.append(newdir) if dirty: self.setDirty(True) @@ -2057,7 +2058,7 @@ self.__remotefsInterface.dirname(fn) if isRemote else os.path.dirname(fn), - target + target, ): try: if isRemote: @@ -2067,9 +2068,7 @@ if not os.path.isdir(target): os.makedirs(target) - if ( - not isRemote and os.path.exists(targetfile) - ) or ( + if (not isRemote and os.path.exists(targetfile)) or ( isRemote and self.__remotefsInterface.exists(targetfile) ): res = EricMessageBox.yesNo( @@ -2122,7 +2121,6 @@ @param quiet flag indicating quiet operations @type bool """ - # TODO: adapt to remote server # get all relevant filename patterns patterns = [] ignorePatterns = [] @@ -2155,9 +2153,8 @@ return if not FileSystemUtilities.samepath(target, source) and not ( - (not isRemote and os.path.isdir(target)) or ( - isRemote and self.__remotefsInterface.isdir(target) - ) + (not isRemote and os.path.isdir(target)) + or (isRemote and self.__remotefsInterface.isdir(target)) ): try: if isRemote: @@ -2189,9 +2186,7 @@ ) if not FileSystemUtilities.samepath(target, source): try: - if ( - not isRemote and os.path.exists(targetfile) - ) or ( + if (not isRemote and os.path.exists(targetfile)) or ( isRemote and self.__remotefsInterface.exists(targetfile) ): res = EricMessageBox.yesNo( @@ -2208,7 +2203,7 @@ # don't overwrite, carry on with next file if isRemote: - self.__remotefsInterface.shutilCopy(file,target ) + self.__remotefsInterface.shutilCopy(file, target) else: shutil.copy(file, target) except OSError: @@ -2229,7 +2224,6 @@ @param target target directory @type str """ - # TODO: adapt to remote server # first perform the addition of source self.__addSingleDirectory(filetype, source, target, True) @@ -2280,7 +2274,6 @@ @param startdir start directory for the selection dialog @type str """ - # TODO: adapt to remote server from .AddDirectoryDialog import AddDirectoryDialog if not startdir: @@ -2321,13 +2314,18 @@ @param fn file name or directory name to add @type str """ - # TODO: adapt to remote server if fn: + separator = ( + self.__remotefsInterface.separator() + if FileSystemUtilities.isRemoteFileName(fn) + else os.sep + ) + # if it is below the project directory, make it relative to that fn = self.getRelativePath(fn) # if it ends with the directory separator character, remove it - if fn.endswith(os.sep): + if fn.endswith(separator): fn = fn[:-1] if fn not in self.__pdata["OTHERS"]: @@ -2335,9 +2333,6 @@ self.othersAdded(fn) self.setDirty(True) - if os.path.isdir(fn) and fn not in self.otherssubdirs: - self.otherssubdirs.append(fn) - def renameMainScript(self, oldfn, newfn): """ Public method to rename the main script. @@ -2367,7 +2362,8 @@ @return flag indicating success @rtype bool """ - # TODO: adapt to remote server + isRemote = FileSystemUtilities.isRemoteFileName(oldfn) + fn = self.getRelativePath(oldfn) isSourceFile = fn in self.__pdata["SOURCES"] @@ -2383,7 +2379,9 @@ return False newfn = FileSystemUtilities.toNativeSeparators(newfn) - if os.path.exists(newfn): + if (not isRemote and os.path.exists(newfn)) or ( + isRemote and self.__remotefsInterface.exists(newfn) + ): res = EricMessageBox.yesNo( self.ui, self.tr("Rename File"), @@ -2397,7 +2395,10 @@ return False try: - os.rename(oldfn, newfn) + if isRemote: + self.__remotefsInterface.rename(oldfn, newfn) + else: + os.rename(oldfn, newfn) except OSError as msg: EricMessageBox.critical( self.ui, @@ -2426,9 +2427,15 @@ even if it doesn't have the source extension @type bool """ - # TODO: adapt to remote server + if FileSystemUtilities.isRemoteFileName(oldname): + oldDirName = self.__remotefsInterface.dirname(oldname) + newDirName = self.__remotefsInterface.dirname(newname) + else: + oldDirName = os.path.dirname(oldname) + newDirName = os.path.dirname(newname) + fn = self.getRelativePath(oldname) - if os.path.dirname(oldname) == os.path.dirname(newname): + if oldDirName == newDirName: if self.__isInPdata(oldname): self.removeFile(oldname, False) self.appendFile(newname, isSourceFile, False) @@ -2449,7 +2456,8 @@ @return list of files starting with a common prefix @rtype list of str """ - # TODO: adapt to remote server + isRemote = FileSystemUtilities.isRemoteFileName(self.ppath) + filelist = [] start = self.getRelativePath(start) for fileCategory in [ @@ -2457,7 +2465,11 @@ ]: for entry in self.__pdata[fileCategory][:]: if entry.startswith(start): - filelist.append(os.path.join(self.ppath, entry)) + filelist.append( + self.__remotefsInterface.join(self.ppath, entry) + if isRemote + else os.path.join(self.ppath, entry) + ) return filelist def __reorganizeFiles(self): @@ -2465,8 +2477,9 @@ Private method to reorganize files stored in the project. """ reorganized = False - - # init data store for the reorganization + isRemote = FileSystemUtilities.isRemoteFileName(self.ppath) + + # initialize data store for the reorganization newPdata = {} for fileCategory in self.getFileCategories(): newPdata[fileCategory] = [] @@ -2475,7 +2488,11 @@ for fileCategory in self.getFileCategories(): for fn in self.__pdata[fileCategory][:]: filetype = fileCategory - bfn = os.path.basename(fn) + bfn = ( + self.__remotefsInterface.basename(fn) + if isRemote + else os.path.basename(fn) + ) for pattern in sorted(self.__pdata["FILETYPES"], reverse=True): if fnmatch.fnmatch(bfn, pattern): filetype = self.__pdata["FILETYPES"][pattern] @@ -2504,6 +2521,8 @@ @param newdn new directory name @type str """ + isRemote = FileSystemUtilities.isRemoteFileName(self.ppath) + olddn = self.getRelativePath(olddn) newdn = self.getRelativePath(newdn) for fileCategory in [ @@ -2513,7 +2532,10 @@ if entry.startswith(olddn): entry = entry.replace(olddn, newdn) self.appendFile( - os.path.join(self.ppath, entry), fileCategory == "SOURCES" + self.__remotefsInterface.join(self.ppath, entry) + if isRemote + else os.path.join(self.ppath, entry), + fileCategory == "SOURCES", ) self.setDirty(True) @@ -2539,12 +2561,8 @@ self.__pdata[fileCategory].remove(entry) entry = entry.replace(olddn, newdn) self.__pdata[fileCategory].append(entry) - if fileCategory == "OTHERS": - if newdn not in self.otherssubdirs: - self.otherssubdirs.append(newdn) - else: - if newdn not in self.subdirs: - self.subdirs.append(newdn) + if fileCategory != "OTHERS" and newdn not in self.subdirs: + self.subdirs.append(newdn) if typeStrings: # the directory is controlled by the project self.setDirty(True) @@ -2586,13 +2604,19 @@ @param dn directory name to be removed from the project @type str """ + separator = ( + self.__remotefsInterface.separator() + if FileSystemUtilities.isRemoteFileName(self.ppath) + else os.sep + ) + dirty = False dn = self.getRelativePath(dn) for entry in self.__pdata["OTHERS"][:]: if entry.startswith(dn): self.__pdata["OTHERS"].remove(entry) dirty = True - dn2 = dn if dn.endswith(os.sep) else dn + os.sep + dn2 = dn if dn.endswith(separator) else dn + separator for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: for entry in self.__pdata[fileCategory][:]: if entry.startswith(dn2): @@ -2613,26 +2637,38 @@ @rtype bool """ try: - os.remove(os.path.join(self.ppath, fn)) - path, ext = os.path.splitext(fn) - if ext == ".ui": - fn2 = os.path.join(self.ppath, "{0}.h".format(fn)) - if os.path.isfile(fn2): - os.remove(fn2) - head, tail = os.path.split(path) - for ext in [".pyc", ".pyo"]: - fn2 = os.path.join(self.ppath, path + ext) - if os.path.isfile(fn2): - os.remove(fn2) - pat = os.path.join( - self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext) + if FileSystemUtilities.isRemoteFileName(self.ppath): + self.__remotefsInterface.remove( + self.__remotefsInterface.join(self.ppath, fn) ) - for f in glob.glob(pat): - os.remove(f) + filepath = self.__remotefsInterface.splitext(fn)[0] + head, tail = self.__remotefsInterface.split(filepath) + for ext in [".pyc", ".pyo"]: + fn2 = self.__remotefsInterface.join(self.ppath, filepath + ext) + if self.__remotefsInterface.isfile(fn2): + self.__remotefsInterface.remove(fn2) + pat = self.__remotefsInterface.join( + self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext) + ) + for f in self.__remotefsInterface.glob(pat): + self.__remotefsInterface.remove(f) + else: + os.remove(os.path.join(self.ppath, fn)) + filepath = os.path.splitext(fn)[0] + head, tail = os.path.split(filepath) + for ext in [".pyc", ".pyo"]: + fn2 = os.path.join(self.ppath, filepath + ext) + if os.path.isfile(fn2): + os.remove(fn2) + pat = os.path.join( + self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext) + ) + for f in glob.glob(pat): + os.remove(f) except OSError as err: EricMessageBox.critical( self.ui, - self.tr("Delete file"), + self.tr("Delete File"), self.tr( "<p>The selected file <b>{0}</b> could not be" " deleted.</p><p>Reason: {1}</p>" @@ -2641,8 +2677,6 @@ return False self.removeFile(fn) - if ext == ".ui": - self.removeFile(fn + ".h") return True def deleteDirectory(self, dn): @@ -2654,14 +2688,16 @@ @return flag indicating success @rtype bool """ - if not os.path.isabs(dn): - dn = os.path.join(self.ppath, dn) + dn = self.getAbsolutePath(dn) try: - shutil.rmtree(dn, ignore_errors=True) + if FileSystemUtilities.isRemoteFileName(dn): + self.__remotefsInterface.shutilRmtree(dn, ignore_errors=True) + else: + shutil.rmtree(dn, ignore_errors=True) except OSError as err: EricMessageBox.critical( self.ui, - self.tr("Delete directory"), + self.tr("Delete Directory"), self.tr( "<p>The selected directory <b>{0}</b> could not be" " deleted.</p><p>Reason: {1}</p>" @@ -2695,6 +2731,7 @@ This method displays the new project dialog and initializes the project object with the data entered. """ + # assume remote project without VCS if connected to server from eric7.VCS.CommandOptionsDialog import VcsCommandOptionsDialog from .PropertiesDialog import PropertiesDialog @@ -2702,7 +2739,9 @@ if not self.checkDirty(): return - dlg = PropertiesDialog(self, new=True) + isRemote = self.__remoteServer.isServerConnected() + + dlg = PropertiesDialog(self, new=True, isRemote=isRemote) if dlg.exec() == QDialog.DialogCode.Accepted: self.closeProject() @@ -2725,7 +2764,7 @@ ) self.actGrp2.setEnabled(True) self.propsAct.setEnabled(True) - self.userPropsAct.setEnabled(True) + self.userPropsAct.setEnabled(not isRemote) self.filetypesAct.setEnabled(True) self.lexersAct.setEnabled(True) self.sessActGrp.setEnabled(False) @@ -2735,14 +2774,30 @@ self.menuCheckAct.setEnabled(True) self.menuShowAct.setEnabled(True) self.menuDiagramAct.setEnabled(True) - self.menuApidocAct.setEnabled(True) + self.menuApidocAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.menuPackagersAct.setEnabled(True) - self.pluginGrp.setEnabled(self.__pdata["PROJECTTYPE"] in ["E7Plugin"]) + self.pluginGrp.setEnabled( + self.__pdata["PROJECTTYPE"] in ["E7Plugin"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.addLanguageAct.setEnabled(bool(self.__pdata["TRANSLATIONPATTERN"])) - self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) - self.menuMakeAct.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) + self.makeGrp.setEnabled( + self.__pdata["MAKEPARAMS"]["MakeEnabled"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) + self.menuMakeAct.setEnabled( + self.__pdata["MAKEPARAMS"]["MakeEnabled"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.menuOtherToolsAct.setEnabled(True) - self.menuFormattingAct.setEnabled(True) + self.menuFormattingAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) + self.menuVcsAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.projectAboutToBeCreated.emit() @@ -2762,9 +2817,17 @@ } # create the project directory if it doesn't exist already - if not os.path.isdir(self.ppath): + ppathExists = ( + self.__remotefsInterface.isdir(self.ppath) + if isRemote + else os.path.isdir(self.ppath) + ) + if not ppathExists: try: - os.makedirs(self.ppath) + if isRemote: + self.__remotefsInterface.makedirs(self.ppath) + else: + os.makedirs(self.ppath) except OSError: EricMessageBox.critical( self.ui, @@ -2782,23 +2845,41 @@ # create an empty __init__.py file to make it a Python package # (only for Python and Python3) if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: - fn = os.path.join(self.ppath, "__init__.py") - with open(fn, "w", encoding="utf-8"): - pass + if isRemote: + fn = self.__remotefsInterface.join(self.ppath, "__init__.py") + self.__remotefsInterface.writeFile(fn, b"") + else: + fn = os.path.join(self.ppath, "__init__.py") + with open(fn, "w", encoding="utf-8"): + pass self.appendFile(fn, True) # create an empty main script file, if a name was given if self.__pdata["MAINSCRIPT"]: - if not os.path.isabs(self.__pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + if isRemote: + if not self.__remotefsInterface.isabs( + self.__pdata["MAINSCRIPT"] + ): + ms = self.__remotefsInterface.join( + self.ppath, self.__pdata["MAINSCRIPT"] + ) + else: + ms = self.__pdata["MAINSCRIPT"] + self.__remotefsInterface.makedirs( + self.__remotefsInterface.dirname(ms), exist_ok=True + ) + self.__remotefsInterface.writeFile(ms, b"") else: - ms = self.__pdata["MAINSCRIPT"] - os.makedirs(os.path.dirname(ms), exist_ok=True) - with open(ms, "w"): - pass + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + else: + ms = self.__pdata["MAINSCRIPT"] + os.makedirs(os.path.dirname(ms), exist_ok=True) + with open(ms, "w"): + pass self.appendFile(ms, True) - if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: + if self.__pdata["MAKEPARAMS"]["MakeEnabled"] and not isRemote: mf = self.__pdata["MAKEPARAMS"]["MakeFile"] if mf: if not os.path.isabs(mf): @@ -2810,15 +2891,34 @@ pass self.appendFile(mf) - tpd = os.path.join(self.ppath, self.translationsRoot) - if not self.translationsRoot.endswith(os.sep): - tpd = os.path.dirname(tpd) - if not os.path.isdir(tpd): - os.makedirs(tpd, exist_ok=True) - if self.__pdata["TRANSLATIONSBINPATH"]: - tpd = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) + if isRemote: + tpd = self.__remotefsInterface.join( + self.ppath, self.translationsRoot + ) + if not self.translationsRoot.endswith( + self.__remotefsInterface.separator() + ): + tpd = self.__remotefsInterface.dirname(tpd) + if not self.__remotefsInterface.isdir(tpd): + self.__remotefsInterface.makedirs(tpd, exist_ok=True) + if self.__pdata["TRANSLATIONSBINPATH"]: + tpd = self.__remotefsInterface.join( + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] + ) + if not self.__remotefsInterface.isdir(tpd): + self.__remotefsInterface.makedirs(tpd, exist_ok=True) + else: + tpd = os.path.join(self.ppath, self.translationsRoot) + if not self.translationsRoot.endswith(os.sep): + tpd = os.path.dirname(tpd) if not os.path.isdir(tpd): os.makedirs(tpd, exist_ok=True) + if self.__pdata["TRANSLATIONSBINPATH"]: + tpd = os.path.join( + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] + ) + if not os.path.isdir(tpd): + os.makedirs(tpd, exist_ok=True) # create management directory if not present self.createProjectManagementDir() @@ -2843,21 +2943,39 @@ return if self.__pdata["MAINSCRIPT"]: - if not os.path.isabs(self.__pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + if isRemote: + if not self.__remotefsInterface.isabs( + self.__pdata["MAINSCRIPT"] + ): + ms = self.__remotefsInterface.join( + self.ppath, self.__pdata["MAINSCRIPT"] + ) + else: + ms = self.__pdata["MAINSCRIPT"] + msExists = self.__remotefsInterface.exists(ms) else: - ms = self.__pdata["MAINSCRIPT"] - if not os.path.exists(ms): + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + else: + ms = self.__pdata["MAINSCRIPT"] + msExists = os.path.exists(ms) + if not msExists: try: - os.makedirs(os.path.dirname(ms)) - with open(ms, "w"): - pass + if isRemote: + self.__remotefsInterface.makedirs( + self.__remotefsInterface.dirname(ms), exist_ok=True + ) + self.__remotefsInterface.writeFile(ms, b"") + else: + os.makedirs(os.path.dirname(ms), exist_ok=True) + with open(ms, "w"): + pass except OSError as err: EricMessageBox.critical( self.ui, self.tr("Create main script"), self.tr( - "<p>The mainscript <b>{0}</b> could not" + "<p>The main script <b>{0}</b> could not" " be created.<br/>Reason: {1}</p>" ).format(ms, str(err)), ) @@ -2865,7 +2983,7 @@ else: ms = "" - if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: + if self.__pdata["MAKEPARAMS"]["MakeEnabled"] and not isRemote: mf = self.__pdata["MAKEPARAMS"]["MakeFile"] if mf: if not os.path.isabs(mf): @@ -2874,7 +2992,7 @@ mf = os.path.join(self.ppath, Project.DefaultMakefile) if not os.path.exists(mf): try: - os.makedirs(os.path.dirname(mf)) + os.makedirs(os.path.dirname(mf), exist_ok=True) with open(mf, "w"): pass except OSError as err: @@ -2896,89 +3014,102 @@ yesDefault=True, ) if res: - self.newProjectAddFiles(ms) - addAllToVcs = res + self.newProjectAddFiles(ms, isRemote=isRemote) + addAllToVcs = res and not isRemote + # create an empty __init__.py file to make it a Python package # if none exists (only for Python and Python3) if self.__pdata["PROGLANGUAGE"] in ["Python3", "MicroPython"]: - fn = os.path.join(self.ppath, "__init__.py") - if not os.path.exists(fn): - with open(fn, "w", encoding="utf-8"): - pass - self.appendFile(fn, True) + if isRemote: + fn = self.__remotefsInterface.join(self.ppath, "__init__.py") + if not self.__remotefsInterface.exists(fn): + self.__remotefsInterface.writeFile(fn, b"") + self.appendFile(fn, True) + else: + fn = os.path.join(self.ppath, "__init__.py") + if not os.path.exists(fn): + with open(fn, "w", encoding="utf-8"): + pass + self.appendFile(fn, True) self.saveProject() # check, if the existing project directory is already under # VCS control - pluginManager = ericApp().getObject("PluginManager") - for indicator, vcsData in list( - pluginManager.getVcsSystemIndicators().items() - ): - if os.path.exists(os.path.join(self.ppath, indicator)): - if len(vcsData) > 1: - vcsList = [] - for _vcsSystemStr, vcsSystemDisplay in vcsData: - vcsList.append(vcsSystemDisplay) - res, vcs_ok = QInputDialog.getItem( - None, - self.tr("New Project"), - self.tr("Select Version Control System"), - vcsList, - 0, - False, - ) - if vcs_ok: - for vcsSystemStr, vcsSystemDisplay in vcsData: - if res == vcsSystemDisplay: - vcsSystem = vcsSystemStr - break + if not isRemote: + pluginManager = ericApp().getObject("PluginManager") + for indicator, vcsData in list( + pluginManager.getVcsSystemIndicators().items() + ): + if os.path.exists(os.path.join(self.ppath, indicator)): + if len(vcsData) > 1: + vcsList = [] + for _vcsSystemStr, vcsSystemDisplay in vcsData: + vcsList.append(vcsSystemDisplay) + res, vcs_ok = QInputDialog.getItem( + None, + self.tr("New Project"), + self.tr("Select Version Control System"), + vcsList, + 0, + False, + ) + if vcs_ok: + for vcsSystemStr, vcsSystemDisplay in vcsData: + if res == vcsSystemDisplay: + vcsSystem = vcsSystemStr + break + else: + vcsSystem = "None" else: vcsSystem = "None" else: - vcsSystem = "None" - else: - vcsSystem = vcsData[0][1] - self.__pdata["VCS"] = vcsSystem - self.vcs = self.initVCS() - self.setDirty(True) - if self.vcs is not None: - # edit VCS command options - if self.vcs.vcsSupportCommandOptions(): - vcores = EricMessageBox.yesNo( - self.ui, - self.tr("New Project"), - self.tr( - """Would you like to edit the VCS""" - """ command options?""" - ), - ) + vcsSystem = vcsData[0][1] + self.__pdata["VCS"] = vcsSystem + self.vcs = self.initVCS() + self.setDirty(True) + if self.vcs is not None: + # edit VCS command options + if self.vcs.vcsSupportCommandOptions(): + vcores = EricMessageBox.yesNo( + self.ui, + self.tr("New Project"), + self.tr( + """Would you like to edit the VCS""" + """ command options?""" + ), + ) + else: + vcores = False + if vcores: + codlg = VcsCommandOptionsDialog(self.vcs) + if codlg.exec() == QDialog.DialogCode.Accepted: + self.vcs.vcsSetOptions(codlg.getOptions()) + # add project file to repository + if res == 0: + apres = EricMessageBox.yesNo( + self.ui, + self.tr("New Project"), + self.tr( + "Shall the project file be added" + " to the repository?" + ), + yesDefault=True, + ) + if apres: + self.saveProject() + self.vcs.vcsAdd(self.pfile) else: - vcores = False - if vcores: - codlg = VcsCommandOptionsDialog(self.vcs) - if codlg.exec() == QDialog.DialogCode.Accepted: - self.vcs.vcsSetOptions(codlg.getOptions()) - # add project file to repository - if res == 0: - apres = EricMessageBox.yesNo( - self.ui, - self.tr("New project"), - self.tr( - "Shall the project file be added" - " to the repository?" - ), - yesDefault=True, - ) - if apres: - self.saveProject() - self.vcs.vcsAdd(self.pfile) - else: - self.__pdata["VCS"] = "None" - self.saveProject() - break + self.__pdata["VCS"] = "None" + self.saveProject() + break # put the project under VCS control - if self.vcs is None and self.vcsSoftwareAvailable() and self.vcsRequested: + if ( + not isRemote + and self.vcs is None + and self.vcsSoftwareAvailable() + and self.vcsRequested + ): vcsSystemsDict = ( ericApp() .getObject("PluginManager") @@ -3048,25 +3179,38 @@ if self.__pdata["EMBEDDED_VENV"]: self.__createEmbeddedEnvironment() - self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled( + self.__pdata["EMBEDDED_VENV"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.projectOpenedHooks.emit() self.projectOpened.emit() # open the main script if self.__pdata["MAINSCRIPT"]: - if not os.path.isabs(self.__pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + if isRemote: + if not self.__remotefsInterface.isabs(self.__pdata["MAINSCRIPT"]): + ms = self.__remotefsInterface.join( + self.ppath, self.__pdata["MAINSCRIPT"] + ) + else: + ms = self.__pdata["MAINSCRIPT"] else: - ms = self.__pdata["MAINSCRIPT"] + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + else: + ms = self.__pdata["MAINSCRIPT"] self.sourceFile.emit(ms) - def newProjectAddFiles(self, mainscript): + def newProjectAddFiles(self, mainscript, isRemote=False): """ Public method to add files to a new project. @param mainscript name of the mainscript @type str + @param isRemote flag indicating a remote project (defaults to False) + @type bool (optional) """ # Show the file type associations for the user to change self.__showFiletypeAssociations() @@ -3075,54 +3219,113 @@ if self.__pdata["EMBEDDED_VENV"]: environmentPath = self.__findEmbeddedEnvironment() if environmentPath: - # there is already an embeded venv; ignore thie whenn adding files - ignoreList = [os.path.split(environmentPath)[-1]] + # there is already an embedded venv; ignore this when adding files + ignoreList = ( + [self.__remotefsInterface.split(environmentPath)[-1]] + if isRemote + else [os.path.split(environmentPath)[-1]] + ) with EricOverrideCursor(): # search the project directory for files with known extensions for filespec in self.__pdata["FILETYPES"]: - files = FileSystemUtilities.direntries( - self.ppath, - filesonly=True, - pattern=filespec, - ignore=ignoreList, + files = ( + self.__remotefsInterface.direntries( + self.ppath, + filesonly=True, + pattern=filespec, + ignore=ignoreList, + ) + if isRemote + else FileSystemUtilities.direntries( + self.ppath, + filesonly=True, + pattern=filespec, + ignore=ignoreList, + ) ) for file in files: self.appendFile(file) # special handling for translation files if self.translationsRoot: - tpd = os.path.join(self.ppath, self.translationsRoot) - if not self.translationsRoot.endswith(os.sep): - tpd = os.path.dirname(tpd) + if isRemote: + tpd = self.__remotefsInterface.join( + self.ppath, self.translationsRoot + ) + if not self.translationsRoot.endswith(os.sep): + tpd = self.__remotefsInterface.dirname(tpd) + else: + tpd = os.path.join(self.ppath, self.translationsRoot) + if not self.translationsRoot.endswith(os.sep): + tpd = os.path.dirname(tpd) else: tpd = self.ppath tslist = [] if self.__pdata["TRANSLATIONPATTERN"]: - pattern = os.path.basename(self.__pdata["TRANSLATIONPATTERN"]) + pattern = ( + self.__remotefsInterface.basename( + self.__pdata["TRANSLATIONPATTERN"] + ) + if isRemote + else os.path.basename(self.__pdata["TRANSLATIONPATTERN"]) + ) if "%language%" in pattern: pattern = pattern.replace("%language%", "*") else: tpd = self.__pdata["TRANSLATIONPATTERN"].split("%language%")[0] else: pattern = "*.ts" - tslist.extend(FileSystemUtilities.direntries(tpd, True, pattern)) + tslist.extend( + self.__remotefsInterface.direntries(tpd, True, pattern) + if isRemote + else FileSystemUtilities.direntries(tpd, True, pattern) + ) + pattern = self.__binaryTranslationFile(pattern) if pattern: - tslist.extend(FileSystemUtilities.direntries(tpd, True, pattern)) + tslist.extend( + self.__remotefsInterface.direntries(tpd, True, pattern) + if isRemote + else FileSystemUtilities.direntries(tpd, True, pattern) + ) if tslist: - if "_" in os.path.basename(tslist[0]): - # the first entry determines the mainscript name - mainscriptname = ( - os.path.splitext(mainscript)[0] - or os.path.basename(tslist[0]).split("_")[0] - ) - self.__pdata["TRANSLATIONPATTERN"] = os.path.join( - os.path.dirname(tslist[0]), - "{0}_%language%{1}".format( - os.path.basename(tslist[0]).split("_")[0], - os.path.splitext(tslist[0])[1], - ), - ) + hasUnderscore = ( + "_" in self.__remotefsInterface.basename(tslist[0]) + if isRemote + else "_" in os.path.basename(tslist[0]) + ) + if hasUnderscore: + # the first entry determines the main script name + if isRemote: + mainscriptname = ( + self.__remotefsInterface.splitext(mainscript)[0] + or self.__remotefsInterface.basename(tslist[0]).split("_")[ + 0 + ] + ) + self.__pdata[ + "TRANSLATIONPATTERN" + ] = self.__remotefsInterface.join( + self.__remotefsInterface.dirname(tslist[0]), + "{0}_%language%{1}".format( + self.__remotefsInterface.basename(tslist[0]).split("_")[ + 0 + ], + self.__remotefsInterface.splitext(tslist[0])[1], + ), + ) + else: + mainscriptname = ( + os.path.splitext(mainscript)[0] + or os.path.basename(tslist[0]).split("_")[0] + ) + self.__pdata["TRANSLATIONPATTERN"] = os.path.join( + os.path.dirname(tslist[0]), + "{0}_%language%{1}".format( + os.path.basename(tslist[0]).split("_")[0], + os.path.splitext(tslist[0])[1], + ), + ) else: mainscriptname = "" pattern, ok = QInputDialog.getText( @@ -3150,12 +3353,20 @@ self.__pdata["TRANSLATIONS"].append(ts) self.projectFileAdded.emit(ts, "TRANSLATIONS") if self.__pdata["TRANSLATIONSBINPATH"]: - tpd = os.path.join( - self.ppath, self.__pdata["TRANSLATIONSBINPATH"] - ) - pattern = os.path.basename( - self.__pdata["TRANSLATIONPATTERN"] - ).replace("%language%", "*") + if isRemote: + tpd = self.__remotefsInterface.join( + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] + ) + pattern = self.__remotefsInterface.basename( + self.__pdata["TRANSLATIONPATTERN"] + ).replace("%language%", "*") + else: + tpd = os.path.join( + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] + ) + pattern = os.path.basename( + self.__pdata["TRANSLATIONPATTERN"] + ).replace("%language%", "*") pattern = self.__binaryTranslationFile(pattern) qmlist = FileSystemUtilities.direntries(tpd, True, pattern) for qm in qmlist: @@ -3174,30 +3385,55 @@ """ from .PropertiesDialog import PropertiesDialog - dlg = PropertiesDialog(self, new=False) + isRemote = FileSystemUtilities.isRemoteFileName(self.ppath) + dlg = PropertiesDialog(self, new=False, isRemote=isRemote) if dlg.exec() == QDialog.DialogCode.Accepted: fileTypesDict = copy.copy(self.__pdata["FILETYPES"]) dlg.storeData() self.setDirty(True) if self.__pdata["MAINSCRIPT"]: - if not os.path.isabs(self.__pdata["MAINSCRIPT"]): - ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + if isRemote: + if not self.__remotefsInterface.isabs(self.__pdata["MAINSCRIPT"]): + ms = self.__remotefsInterface.join( + self.ppath, self.__pdata["MAINSCRIPT"] + ) + else: + ms = self.__pdata["MAINSCRIPT"] + if self.__remotefsInterface.exists(ms): + self.appendFile(ms) else: - ms = self.__pdata["MAINSCRIPT"] - if os.path.exists(ms): - self.appendFile(ms) + if not os.path.isabs(self.__pdata["MAINSCRIPT"]): + ms = os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + else: + ms = self.__pdata["MAINSCRIPT"] + if os.path.exists(ms): + self.appendFile(ms) if self.__pdata["MAKEPARAMS"]["MakeEnabled"]: mf = self.__pdata["MAKEPARAMS"]["MakeFile"] - if mf: - if not os.path.isabs(mf): - mf = os.path.join(self.ppath, mf) + if isRemote: + if mf: + if not self.__remotefsInterface.isabs(mf): + mf = self.__remotefsInterface.join(self.ppath, mf) + else: + mf = self.__remotefsInterface.join( + self.ppath, Project.DefaultMakefile + ) + exists = self.__remotefsInterface.exists(mf) else: - mf = os.path.join(self.ppath, Project.DefaultMakefile) - if not os.path.exists(mf): + if mf: + if not os.path.isabs(mf): + mf = os.path.join(self.ppath, mf) + else: + mf = os.path.join(self.ppath, Project.DefaultMakefile) + exists = os.path.exists(mf) + if not exists: try: - with open(mf, "w"): - pass + if isRemote: + self.__remotefsInterface.writeFile(mf, b"") + else: + with open(mf, "w"): + pass except OSError as err: EricMessageBox.critical( self.ui, @@ -3210,20 +3446,38 @@ self.appendFile(mf) if self.translationsRoot: - tp = os.path.join(self.ppath, self.translationsRoot) - if not self.translationsRoot.endswith(os.sep): - tp = os.path.dirname(tp) + if isRemote: + tp = self.__remotefsInterface.join( + self.ppath, self.translationsRoot + ) + if not self.translationsRoot.endswith( + self.__remotefsInterface.separator() + ): + tp = self.__remotefsInterface.dirname(tp) + if not self.__remotefsInterface.isdir(tp): + self.__remotefsInterface.makedirs(tp) + else: + tp = os.path.join(self.ppath, self.translationsRoot) + if not self.translationsRoot.endswith(os.sep): + tp = os.path.dirname(tp) + if not os.path.isdir(tp): + os.makedirs(tp) else: tp = self.ppath - if not os.path.isdir(tp): - os.makedirs(tp) if tp != self.ppath and tp not in self.subdirs: self.subdirs.append(tp) if self.__pdata["TRANSLATIONSBINPATH"]: - tp = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) - if not os.path.isdir(tp): - os.makedirs(tp) + if isRemote: + tp = self.__remotefsInterface.join( + self.ppath, self.__pdata["TRANSLATIONSBINPATH"] + ) + if not self.__remotefsInterface.isdir(tp): + self.__remotefsInterface.makedirs(tp) + else: + tp = os.path.join(self.ppath, self.__pdata["TRANSLATIONSBINPATH"]) + if not os.path.isdir(tp): + os.makedirs(tp) if tp != self.ppath and tp not in self.subdirs: self.subdirs.append(tp) @@ -3375,7 +3629,7 @@ if fn is None: fn = EricFileDialog.getOpenFileName( self.parent(), - self.tr("Open project"), + self.tr("Open Project"), Preferences.getMultiProject("Workspace") or OSUtilities.getHomeDir(), self.tr("Project Files (*.epj)"), ) @@ -3410,47 +3664,53 @@ with EricOverrideCursor(): oldState = self.isDirty() self.vcs = self.initVCS() - if self.vcs is None and self.isDirty() == oldState: - # check, if project is version controlled - pluginManager = ericApp().getObject("PluginManager") - for ( - indicator, - vcsData, - ) in pluginManager.getVcsSystemIndicators().items(): - if os.path.exists(os.path.join(self.ppath, indicator)): - if len(vcsData) > 1: - vcsList = [] - for _vcsSystemStr, vcsSystemDisplay in vcsData: - vcsList.append(vcsSystemDisplay) - with EricOverridenCursor(): - res, vcs_ok = QInputDialog.getItem( - None, - self.tr("New Project"), - self.tr("Select Version Control System"), - vcsList, - 0, - False, - ) - if vcs_ok: - for vcsSystemStr, vcsSystemDisplay in vcsData: - if res == vcsSystemDisplay: - vcsSystem = vcsSystemStr - break + if not FileSystemUtilities.isRemoteFileName(self.ppath): + if self.vcs is None and self.isDirty() == oldState: + # check, if project is version controlled + pluginManager = ericApp().getObject("PluginManager") + for ( + indicator, + vcsData, + ) in pluginManager.getVcsSystemIndicators().items(): + if os.path.exists(os.path.join(self.ppath, indicator)): + if len(vcsData) > 1: + vcsList = [] + for _vcsSystemStr, vcsSystemDisplay in vcsData: + vcsList.append(vcsSystemDisplay) + with EricOverridenCursor(): + res, vcs_ok = QInputDialog.getItem( + None, + self.tr("New Project"), + self.tr( + "Select Version Control System" + ), + vcsList, + 0, + False, + ) + if vcs_ok: + for ( + vcsSystemStr, + vcsSystemDisplay, + ) in vcsData: + if res == vcsSystemDisplay: + vcsSystem = vcsSystemStr + break + else: + vcsSystem = "None" else: vcsSystem = "None" else: - vcsSystem = "None" - else: - vcsSystem = vcsData[0][0] - self.__pdata["VCS"] = vcsSystem - self.vcs = self.initVCS() - self.setDirty(True) - if self.vcs is not None and ( - self.vcs.vcsRegisteredState(self.ppath) - != VersionControlState.Controlled - ): - self.__pdata["VCS"] = "None" - self.vcs = self.initVCS() + vcsSystem = vcsData[0][0] + self.__pdata["VCS"] = vcsSystem + self.vcs = self.initVCS() + self.setDirty(True) + if self.vcs is not None and ( + self.vcs.vcsRegisteredState(self.ppath) + != VersionControlState.Controlled + ): + self.__pdata["VCS"] = "None" + self.vcs = self.initVCS() self.reloadAct.setEnabled(True) self.closeAct.setEnabled(True) self.saveasAct.setEnabled(True) @@ -3460,7 +3720,9 @@ ) self.actGrp2.setEnabled(True) self.propsAct.setEnabled(True) - self.userPropsAct.setEnabled(True) + self.userPropsAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.pfile) + ) self.filetypesAct.setEnabled(True) self.lexersAct.setEnabled(True) self.sessActGrp.setEnabled(True) @@ -3470,20 +3732,34 @@ self.menuCheckAct.setEnabled(True) self.menuShowAct.setEnabled(True) self.menuDiagramAct.setEnabled(True) - self.menuApidocAct.setEnabled(True) - self.menuPackagersAct.setEnabled(True) + self.menuApidocAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) + self.menuPackagersAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.pluginGrp.setEnabled( self.__pdata["PROJECTTYPE"] in ["E7Plugin"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) ) self.addLanguageAct.setEnabled( bool(self.__pdata["TRANSLATIONPATTERN"]) ) - self.makeGrp.setEnabled(self.__pdata["MAKEPARAMS"]["MakeEnabled"]) + self.makeGrp.setEnabled( + self.__pdata["MAKEPARAMS"]["MakeEnabled"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.menuMakeAct.setEnabled( self.__pdata["MAKEPARAMS"]["MakeEnabled"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) ) self.menuOtherToolsAct.setEnabled(True) - self.menuFormattingAct.setEnabled(True) + self.menuFormattingAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) + self.menuVcsAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) # open a project debugger properties file being quiet # about errors @@ -3504,7 +3780,10 @@ self.__configureEnvironment(envPath) else: self.__createEmbeddedEnvironment() - self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled( + self.__pdata["EMBEDDED_VENV"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.projectOpenedHooks.emit() self.projectOpened.emit() @@ -3535,7 +3814,8 @@ self.__readSession(quiet=True) # start the VCS monitor thread - self.__vcsConnectStatusMonitor() + if not FileSystemUtilities.isRemoteFileName(self.ppath): + self.__vcsConnectStatusMonitor() else: self.__initData() # delete all invalid data read @@ -3719,7 +3999,10 @@ return False # stop the VCS monitor thread - if self.vcs is not None: + if ( + not FileSystemUtilities.isRemoteFileName(self.ppath) + and self.vcs is not None + ): self.vcs.stopStatusMonitor() # now save the tasks @@ -3729,7 +4012,7 @@ self.ui.taskViewer.setProjectOpen(False) # now shutdown the vcs interface - if self.vcs: + if not FileSystemUtilities.isRemoteFileName(self.ppath) and self.vcs: self.vcs.vcsShutdown() self.vcs.deleteLater() self.vcs = None @@ -3795,7 +4078,7 @@ if reportSyntaxErrors and filesWithSyntaxErrors > 0: EricMessageBox.critical( self.ui, - self.tr("Syntax errors detected"), + self.tr("Syntax Errors Detected"), self.tr( """The project contains %n file(s) with syntax errors.""", "", @@ -3831,7 +4114,7 @@ if reportSyntaxErrors and filesWithSyntaxErrors > 0: EricMessageBox.critical( self.ui, - self.tr("Syntax errors detected"), + self.tr("Syntax Errors Detected"), self.tr( """The project contains %n file(s) with syntax errors.""", "", @@ -3856,7 +4139,12 @@ """ if self.__pdata["MAINSCRIPT"]: if normalized: - return os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) + if FileSystemUtilities.isRemoteFileName(self.ppath): + return self.__remotefsInterface.join( + self.ppath, self.__pdata["MAINSCRIPT"] + ) + else: + return os.path.join(self.ppath, self.__pdata["MAINSCRIPT"]) else: return self.__pdata["MAINSCRIPT"] else: @@ -3889,7 +4177,13 @@ raise ValueError("Given file type has incorrect value.") if normalized: - return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] + if FileSystemUtilities.isRemoteFileName(self.ppath): + return [ + self.__remotefsInterface.join(self.ppath, fn) + for fn in self.__pdata[fileType] + ] + else: + return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] else: return self.__pdata[fileType] @@ -3984,16 +4278,18 @@ @rtype tuple of (str, str) """ pwl = "" - if self.__pdata["SPELLWORDS"]: - pwl = os.path.join(self.ppath, self.__pdata["SPELLWORDS"]) - if not os.path.isfile(pwl): - pwl = "" - pel = "" - if self.__pdata["SPELLEXCLUDES"]: - pel = os.path.join(self.ppath, self.__pdata["SPELLEXCLUDES"]) - if not os.path.isfile(pel): - pel = "" + + if not FileSystemUtilities.isRemoteFileName(self.ppath): + if self.__pdata["SPELLWORDS"]: + pwl = os.path.join(self.ppath, self.__pdata["SPELLWORDS"]) + if not os.path.isfile(pwl): + pwl = "" + + if self.__pdata["SPELLEXCLUDES"]: + pel = os.path.join(self.ppath, self.__pdata["SPELLEXCLUDES"]) + if not os.path.isfile(pel): + pel = "" return (pwl, pel) @@ -4019,25 +4315,30 @@ """ return self.ppath - def startswithProjectPath(self, path): + def startswithProjectPath(self, checkpath): """ Public method to check, if a path starts with the project path. - @param path path to be checked + @param checkpath path to be checked @type str @return flag indicating that the path starts with the project path @rtype bool """ - return bool(self.ppath) and ( - path == self.ppath - or FileSystemUtilities.normcasepath( - FileSystemUtilities.toNativeSeparators(path) - ).startswith( - FileSystemUtilities.normcasepath( - FileSystemUtilities.toNativeSeparators(self.ppath + "/") + if FileSystemUtilities.isRemoteFileName(self.ppath): + return checkpath == self.ppath or checkpath.startswith( + self.ppath + self.__remotefsInterface.separator() + ) + else: + return bool(self.ppath) and ( + checkpath == self.ppath + or FileSystemUtilities.normcasepath( + FileSystemUtilities.toNativeSeparators(checkpath) + ).startswith( + FileSystemUtilities.normcasepath( + FileSystemUtilities.toNativeSeparators(self.ppath + "/") + ) ) ) - ) def getProjectFile(self): """ @@ -4096,37 +4397,39 @@ """ return self.__pdata["HASH"] - def getRelativePath(self, path): + def getRelativePath(self, fullpath): """ Public method to convert a file path to a project relative file path. - @param path file or directory name to convert + @param fullpath file or directory name to convert @type str @return project relative path or unchanged path, if path doesn't belong to the project @rtype str """ - if path is None: + if fullpath is None: return "" try: if FileSystemUtilities.isRemoteFileName(self.ppath): if self.__remotefsInterface.separator() == "\\": - return str(pathlib.PureWindowsPath(path).relative_to(self.ppath)) + return str( + pathlib.PureWindowsPath(fullpath).relative_to(self.ppath) + ) else: - return str(pathlib.PurePosixPath(path).relative_to(self.ppath)) + return str(pathlib.PurePosixPath(fullpath).relative_to(self.ppath)) else: - return str(pathlib.PurePath(path).relative_to(self.ppath)) + return str(pathlib.PurePath(fullpath).relative_to(self.ppath)) except ValueError: - return path - - def getRelativeUniversalPath(self, path): + return fullpath + + def getRelativeUniversalPath(self, fullpath): """ Public method to convert a file path to a project relative file path with universal separators. - @param path file or directory name to convert + @param fullpath file or directory name to convert @type str @return project relative path or unchanged path, if path doesn't belong to the project @@ -4134,10 +4437,12 @@ """ if FileSystemUtilities.isRemoteFileName(self.ppath): return self.__remotefsInterface.fromNativeSeparators( - self.getRelativePath(path) + self.getRelativePath(fullpath) ) else: - return FileSystemUtilities.fromNativeSeparators(self.getRelativePath(path)) + return FileSystemUtilities.fromNativeSeparators( + self.getRelativePath(fullpath) + ) def getAbsolutePath(self, fn): """ @@ -4254,6 +4559,7 @@ @return name of the project's virtual environment @rtype str """ + # TODO: remote server not supported yet venvName = ( self.__venvConfiguration["name"] if self.__pdata["EMBEDDED_VENV"] and bool(self.__venvConfiguration["name"]) @@ -4275,6 +4581,7 @@ @return path name of the embedded virtual environment @rtype str """ + # TODO: remote server not supported yet if self.__pdata["EMBEDDED_VENV"]: return self.__findEmbeddedEnvironment() else: @@ -4291,6 +4598,7 @@ @return path of the project's interpreter @rtype str """ + # TODO: remote server not supported yet interpreter = ( self.__venvConfiguration["interpreter"] if self.__pdata["EMBEDDED_VENV"] @@ -4316,6 +4624,7 @@ @return executable search path prefix @rtype str """ + # TODO: remote server not supported yet if self.__pdata["EMBEDDED_VENV"]: execPath = self.__venvConfiguration["exec_path"] else: @@ -4337,6 +4646,7 @@ @return testing framework name of the project @rtype str """ + # TODO: remote server not supported yet try: return self.__pdata["TESTING_FRAMEWORK"] except KeyError: @@ -4364,7 +4674,11 @@ @return flag indicating membership @rtype bool """ - newfn = os.path.abspath(fn) + newfn = ( + self.__remotefsInterface.abspath(fn) + if FileSystemUtilities.isRemoteFileName(self.ppath) + else os.path.abspath(fn) + ) newfn = self.getRelativePath(newfn) return any( newfn in self.__pdata[category] for category in self.getFileCategories() @@ -4397,7 +4711,11 @@ @return flag indicating membership @rtype bool """ - newfn = fn if FileSystemUtilities.isRemoteFileName(fn) else os.path.abspath(fn) + newfn = ( + self.__remotefsInterface.abspath(fn) + if FileSystemUtilities.isRemoteFileName(fn) + else os.path.abspath(fn) + ) newfn = self.getRelativePath(newfn) if newfn in self.__pdata[group] or ( group == "OTHERS" @@ -4405,7 +4723,10 @@ ): return True - if OSUtilities.isWindowsPlatform(): + if ( + OSUtilities.isWindowsPlatform() + or self.__remotefsInterface.separator() == "\\" + ): # try the above case-insensitive newfn = newfn.lower() if any(entry.lower() == newfn for entry in self.__pdata[group]): @@ -5694,7 +6015,7 @@ # build the project tools menu toolsMenu.setTearOffEnabled(True) toolsMenu.addSeparator() - toolsMenu.addMenu(self.vcsMenu) + self.menuVcsAct = toolsMenu.addMenu(self.vcsMenu) toolsMenu.addSeparator() self.menuCheckAct = toolsMenu.addMenu(self.checksMenu) toolsMenu.addSeparator() @@ -5768,7 +6089,10 @@ Private method to set up the project menu. """ self.menuRecentAct.setEnabled(len(self.recent) > 0) - self.menuEnvironmentAct.setEnabled(self.__pdata["EMBEDDED_VENV"]) + self.menuEnvironmentAct.setEnabled( + self.__pdata["EMBEDDED_VENV"] + and not FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.showMenu.emit("Main", self.__menus["Main"]) @@ -5778,7 +6102,9 @@ with the central store. """ for recent in self.recent[:]: - if FileSystemUtilities.samepath(self.pfile, recent): + if ( + FileSystemUtilities.isRemoteFileName(recent) and recent == self.pfile + ) or FileSystemUtilities.samepath(self.pfile, recent): self.recent.remove(recent) self.recent.insert(0, self.pfile) maxRecent = Preferences.getProject("RecentNumber") @@ -5798,11 +6124,22 @@ formatStr = "&{0:d}. {1}" if idx < 10 else "{0:d}. {1}" act = self.recentMenu.addAction( formatStr.format( - idx, FileSystemUtilities.compactPath(rp, self.ui.maxMenuFilePathLen) + idx, + self.__remotefsInterface.compactPath(rp, self.ui.maxMenuFilePathLen) + if FileSystemUtilities.isRemoteFileName(rp) + else FileSystemUtilities.compactPath( + rp, self.ui.maxMenuFilePathLen + ), ) ) act.setData(rp) - act.setEnabled(pathlib.Path(rp).exists()) + if FileSystemUtilities.isRemoteFileName(rp): + act.setEnabled( + self.__remoteServer.isServerConnected + and self.__remotefsInterface.exists(rp) + ) + else: + act.setEnabled(pathlib.Path(rp).exists()) self.recentMenu.addSeparator() self.recentMenu.addAction(self.tr("&Clear"), self.clearRecent) @@ -5853,7 +6190,9 @@ self.__findProjectFileDialog.sourceFile.connect(self.sourceFile) self.__findProjectFileDialog.designerFile.connect(self.designerFile) self.__findProjectFileDialog.linguistFile.connect(self.linguistFile) - self.__findProjectFileDialog.show() + self.__findProjectFileDialog.show( + FileSystemUtilities.isRemoteFileName(self.ppath) + ) self.__findProjectFileDialog.raise_() self.__findProjectFileDialog.activateWindow() @@ -5879,13 +6218,15 @@ recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") newFiles = [] + isRemote = FileSystemUtilities.isRemoteFileName(self.ppath) + ignore_patterns = [ pattern for pattern, filetype in self.__pdata["FILETYPES"].items() if filetype == "__IGNORE__" ] - dirs = self.subdirs[:] + dirs = [""] if recursiveSearch else self.subdirs[:] + [""] for directory in dirs: skip = False for ignore_pattern in ignore_patterns: @@ -5895,9 +6236,17 @@ if skip: continue - curpath = os.path.join(self.ppath, directory) + curpath = ( + self.__remotefsInterface.join(self.ppath, directory) + if isRemote + else os.path.join(self.ppath, directory) + ) try: - newSources = os.listdir(curpath) + newSources = ( + [e["name"] for e in self.__remotefsInterface.listdir(curpath)[2]] + if isRemote + else os.listdir(curpath) + ) except OSError: newSources = [] pattern = ( @@ -5908,24 +6257,30 @@ binpattern = self.__binaryTranslationFile(pattern) for ns in newSources: # ignore hidden files and directories - if ns.startswith("."): - continue - if ( - OSUtilities.isWindowsPlatform() - and os.path.isdir(os.path.join(curpath, ns)) - and ns.startswith("_") - ): - # dot net hack + if ns.startswith(".") or ns == "__pycache__": continue # set fn to project relative name # then reset ns to fully qualified name for insertion, # possibly. - fn = os.path.join(directory, ns) if directory else ns - ns = os.path.abspath(os.path.join(curpath, ns)) + if isRemote: + fn = ( + self.__remotefsInterface.join(directory, ns) + if directory + else ns + ) + ns = self.__remotefsInterface.abspath( + self.__remotefsInterface.join(curpath, ns) + ) + + isdir_ns = self.__remotefsInterface.isdir(ns) + else: + fn = os.path.join(directory, ns) if directory else ns + ns = os.path.abspath(os.path.join(curpath, ns)) + isdir_ns = os.path.isdir(ns) # do not bother with dirs here... - if os.path.isdir(ns): + if isdir_ns: if recursiveSearch: d = self.getRelativePath(ns) if d not in dirs: @@ -5933,7 +6288,11 @@ continue filetype = "" - bfn = os.path.basename(fn) + bfn = ( + self.__remotefsInterface.basename(fn) + if isRemote + else os.path.basename(fn) + ) for pattern in sorted(self.__pdata["FILETYPES"], reverse=True): if fnmatch.fnmatch(bfn, pattern): filetype = self.__pdata["FILETYPES"][pattern] @@ -6256,11 +6615,19 @@ """ from eric7.DataViews.CodeMetricsDialog import CodeMetricsDialog - files = [ - os.path.join(self.ppath, file) - for file in self.__pdata["SOURCES"] - if file.endswith(".py") - ] + files = ( + [ + self.__remotefsInterface.join(self.ppath, file) + for file in self.__pdata["SOURCES"] + if file.endswith(".py") + ] + if FileSystemUtilities.isRemoteFileName(self.ppath) + else [ + os.path.join(self.ppath, file) + for file in self.__pdata["SOURCES"] + if file.endswith(".py") + ] + ) self.codemetrics = CodeMetricsDialog() self.codemetrics.show() self.codemetrics.prepare(files) @@ -6302,11 +6669,19 @@ else: return - files = [ - os.path.join(self.ppath, file) - for file in self.__pdata["SOURCES"] - if os.path.splitext(file)[1].startswith(".py") - ] + files = ( + [ + self.__remotefsInterface.join(self.ppath, file) + for file in self.__pdata["SOURCES"] + if self.__remotefsInterface.splitext(file)[1].startswith(".py") + ] + if FileSystemUtilities.isRemoteFileName(self.ppath) + else [ + os.path.join(self.ppath, file) + for file in self.__pdata["SOURCES"] + if os.path.splitext(file)[1].startswith(".py") + ] + ) self.codecoverage = PyCoverageDialog() self.codecoverage.show() self.codecoverage.start(fn, files) @@ -6917,7 +7292,9 @@ @return flag indicating enabled make support @rtype bool """ - return self.__pdata["MAKEPARAMS"]["MakeEnabled"] + return self.__pdata["MAKEPARAMS"][ + "MakeEnabled" + ] and not FileSystemUtilities.isRemoteFileName(self.ppath) @pyqtSlot() def __autoExecuteMake(self): @@ -6925,7 +7302,9 @@ Private slot to execute a project specific make run (auto-run) (execute or question). """ - if Preferences.getProject("AutoExecuteMake"): + if Preferences.getProject( + "AutoExecuteMake" + ) and not FileSystemUtilities.isRemoteFileName(self.ppath): self.__executeMake( questionOnly=self.__pdata["MAKEPARAMS"]["MakeTestOnly"], interactive=False, @@ -6942,6 +7321,14 @@ through a menu action) @type bool """ + if FileSystemUtilities.isRemoteFileName(self.ppath): + EricMessageBox.critical( + self.ui, + self.tr("Execute Make"), + self.tr("'Make' is not supported for remote projects. Aborting..."), + ) + return + if ( not self.__pdata["MAKEPARAMS"]["MakeEnabled"] or self.__makeProcess is not None @@ -7167,6 +7554,10 @@ """ Private slot called before the 'Other Tools' menu is shown. """ + self.createSBOMAct.setEnabled( + not FileSystemUtilities.isRemoteFileName(self.ppath) + ) + self.showMenu.emit("OtherTools", self.othersMenu) @pyqtSlot()