Repository: spyder-ide/spyder

Issue:
Spyder editor does not open previously opened files in a project
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [ ] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [ ] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description
Spyder editor does not open previously opened files in a project.

At any time, I may switch between projects. I would close project A, and open project B. But if I want to return to work on project A, I find that the editor does not automatically open all the files that was opened before I closed project A the last time.

Only when I close Spyder entirely, then will the project state be saved.


### What steps reproduce the problem?

1. Install anaconda
2. Open spyder
3. Create a project with files in them. 
4. Open the project, open some saved file Z in editor. Close all other files.
5. Close project.
6. Open project again.
7. Check if file Z is opened in editor.

### What is the expected output? What do you see instead?
File Z should be opened. It is not opened.


* Spyder version: 3.3.1
* Python version: 3.5.4 64 bits
* Qt version: 5.6.2
* PyQt version: 5.6
* Operating System name/version: Windows 8.1



Discussion:
Thanks for reporting @jhoutword. I can reproduce this in latest master also. I will investigate and see if I can fix this quickly.
@ccordoba12 Milestone?
I think this was introduced here: https://github.com/spyder-ide/spyder/commit/3d7a94278f7bb5d58d35b9ff0e33e00519dfc594#diff-271b198d60769127e85929e3fcd8f61a, so in version 3.2.7.

The problem is that calling `self.set_project_filenames(self.editor.get_open_filenames())` after we've set `self.current_active_project` to  `None` does nothing because of the `if` condition :

```python
def set_project_filenames(self, recent_files):
    """Set the list of open file names in a project"""
    if (self.current_active_project
            and self.is_valid_project(
                    self.current_active_project.root_path)):
        self.current_active_project.set_recent_files(recent_files)
```

To fix that, I think that we need to call `self.set_project_filenames(self.editor.get_open_filenames())`  before we set `self.current_active_project` to  `None`. So this should look something like that in master:

```python
def close_project(self):
    """
    Close current project and return to a window without an active
    project
    """
    if self.current_active_project:
        self.switch_to_plugin()
        path = self.current_active_project.root_path
        if self.main.editor is not None:
            self.set_project_filenames(
                self.main.editor.get_open_filenames())
        self.current_active_project = None
        self.set_option('current_project_path', None)
        self.setup_menu_actions()

        self.sig_project_closed.emit(path)
        self.sig_pythonpath_changed.emit()

        if self.dockwidget is not None:
            self.set_option('visible_if_project_open',
                            self.dockwidget.isVisible())
            self.dockwidget.close()

        self.explorer.clear()
        self.restart_consoles()
```

@ccordoba12 What do you think? Was there any reason to move `self.set_project_filenames(self.editor.get_open_filenames())` at the end of the `close_project` method when you did your changes?
I've updated the reason why the changes are causing the bug in my post above, but the proposed fix remain the same.
Note that this bug does not occur when **switching** projects, only when closing, then re-opening a given project.
> Was there any reason to move self.set_project_filenames(self.editor.get_open_filenames()) at the end of the close_project method when you did your changes?

I was trying to make the plugin easier to test (at least that's what I recall from the commit message). But I iterated a lot over that during the last months, so the things I changed in 3.2.7 could be reverted (just guessing).
That is what I thought, I just wanted to be sure. I'll submit a PR against Master to fix this and maybe add a regression test to cover this.

Resolving patch:
diff --git a/spyder/plugins/projects/plugin.py b/spyder/plugins/projects/plugin.py
--- a/spyder/plugins/projects/plugin.py
+++ b/spyder/plugins/projects/plugin.py
@@ -314,6 +314,9 @@ def close_project(self):
         """
         if self.current_active_project:
             self.switch_to_plugin()
+            if self.main.editor is not None:
+                self.set_project_filenames(
+                    self.main.editor.get_open_filenames())
             path = self.current_active_project.root_path
             self.current_active_project = None
             self.set_option('current_project_path', None)
@@ -330,10 +333,6 @@ def close_project(self):
             self.explorer.clear()
             self.restart_consoles()
 
-            if self.main.editor is not None:
-                self.set_project_filenames(
-                    self.main.editor.get_open_filenames())
-
     def _delete_project(self):
         """Delete current project."""
         if self.current_active_project:


Test patch:
diff --git a/spyder/plugins/projects/tests/test_plugin.py b/spyder/plugins/projects/tests/test_plugin.py
--- a/spyder/plugins/projects/tests/test_plugin.py
+++ b/spyder/plugins/projects/tests/test_plugin.py
@@ -26,15 +26,26 @@
 
 
 # =============================================================================
-# Fixtures
+# ---- Fixtures
 # =============================================================================
 @pytest.fixture
 def projects(qtbot, mocker):
     """Projects plugin fixture."""
 
+    class EditorMock(object):
+        def get_open_filenames(self):
+            # Patch this with mocker to return a different value.
+            # See test_set_project_filenames_in_close_project.
+            return []
+
+        def __getattr__(self, attr):
+            return Mock()
+
     class MainWindowMock(object):
+        editor = EditorMock()
+
         def __getattr__(self, attr):
-            if attr == 'ipyconsole' or attr == 'editor':
+            if attr == 'ipyconsole':
                 return None
             else:
                 return Mock()
@@ -57,8 +68,33 @@ def __getattr__(self, attr):
     return projects
 
 
+@pytest.fixture
+def create_projects(projects, mocker):
+    """Create a Projects plugin fixture"""
+    def _create_projects(path, files):
+        """
+        Using the Projects plugin fixture, open a project at the
+        specified path, and mock the opening of the specified files
+        in the Editor.
+        """
+        # Open a project.
+        projects.open_project(path=path)
+
+        # Mock the opening of files in the Editor while the project is open.
+        mocker.patch.object(
+            projects.main.editor, 'get_open_filenames', return_value=files)
+
+        # We mock os.path.isfile so that we do not have to
+        # actually create the files on the disk.
+        mocker.patch(
+            'spyder.plugins.projects.api.os.path.isfile', return_value=True)
+
+        return projects
+    return _create_projects
+
+
 # =============================================================================
-# Tests
+# ---- Tests
 # =============================================================================
 @pytest.mark.parametrize("test_directory", [u'測試', u'اختبار', u"test_dir"])
 def test_open_project(projects, tmpdir, test_directory):
@@ -120,5 +156,49 @@ def test_open_project_uses_visible_config(projects, tmpdir, value):
     assert projects.dockwidget.isVisible() == value
 
 
+def test_set_get_project_filenames_when_closing(create_projects, tmpdir):
+    """
+    Test that the currently opened files in the Editor are saved and loaded
+    correctly to and from the project config when the project is closed and
+    then reopened.
+
+    Regression test for Issue #8375
+    """
+    opened_files = ['file1', 'file2', 'file3']
+    path = to_text_string(tmpdir.mkdir('project1'))
+
+    # Create the projects plugin.
+    projects = create_projects(path, opened_files)
+    assert projects.get_project_filenames() == []
+
+    # Close and reopen the project.
+    projects.close_project()
+    projects.open_project(path=path)
+    assert projects.get_project_filenames() == opened_files
+
+
+def test_set_get_project_filenames_when_switching(create_projects, tmpdir):
+    """
+    Test that files in the Editor are loaded and saved correctly when
+    switching projects.
+    """
+    opened_files = ['file1', 'file2', 'file3']
+    path1 = to_text_string(tmpdir.mkdir('project1'))
+    path2 = to_text_string(tmpdir.mkdir('project2'))
+
+    # Create the projects plugin.
+    projects = create_projects(path1, opened_files)
+    assert projects.get_project_filenames() == []
+
+    # Switch to another project.
+    projects.open_project(path=path2)
+    assert projects.get_project_filenames() == []
+
+    # Switch back to the first project.
+    projects.close_project()
+    projects.open_project(path=path1)
+    assert projects.get_project_filenames() == opened_files
+
+
 if __name__ == "__main__":
     pytest.main()