src/eric7/Project/Project.py

branch
server
changeset 10610
bb0149571d94
parent 10605
b6f5e27daeb5
child 10631
00f5aae565a3
--- 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()

eric ide

mercurial