Repository: spyder-ide/spyder

Issue:
Problem when closing files with split editors whose tabs order are not synced.
Related to Issue #4865 and #4831

----

## Description of your problem

**What steps will reproduce the problem?**

1. Split the Editor and open more than one file
2. Move file position (tab) in the first editor
3. Close a file in the first editor whose position has been changed in 3.

**What is the expected output? What do you see instead?**

The same file should be closed in both editor. Instead, the wrong file is being closed in the second editor.

![close_file_in_split_bug](https://user-images.githubusercontent.com/10170372/30543431-21f40ed2-9c51-11e7-9ce5-9dab514f804d.gif)

**Please provide any additional information below**


## Versions and main components

* Spyder Version: 4.0.0.dev0
* Python Version: 3.6.1
* Qt Version: 5.9.1
* PyQt Version: 5.9
* Operating system: Windows 7 64 bits


## Dependencies

IPython >=4.0    :  6.1.0 (OK)
cython >=0.21    :  0.25.2 (OK)
jedi >=0.9.0     :  0.10.2 (OK)
nbconvert >=4.0  :  5.2.1 (OK)
numpy >=1.7      :  1.13.0 (OK)
pandas >=0.13.1  :  0.20.3 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pyflakes >=0.6.0 :  1.5.0 (OK)
pygments >=2.0   :  2.2.0 (OK)
pylint >=0.25    :  1.7.1 (OK)
qtconsole >=4.2.0:  4.4.0.dev (OK)
rope >=0.9.4     :  0.10.7 (OK)
sphinx >=0.6.6   :  1.6.2 (OK)
sympy >=0.7.3    :  None (NOK)




Discussion:

Resolving patch:
diff --git a/spyder/plugins/editor.py b/spyder/plugins/editor.py
--- a/spyder/plugins/editor.py
+++ b/spyder/plugins/editor.py
@@ -1330,7 +1330,6 @@ def register_editorstack(self, editorstack):
         editorstack.sig_prev_edit_pos.connect(self.go_to_last_edit_location)
         editorstack.sig_prev_cursor.connect(self.go_to_previous_cursor_position)
         editorstack.sig_next_cursor.connect(self.go_to_next_cursor_position)
-        editorstack.tabs.tabBar().tabMoved.connect(self.move_editorstack_data)
 
     def unregister_editorstack(self, editorstack):
         """Removing editorstack only if it's not the last remaining"""
@@ -1348,11 +1347,12 @@ def clone_editorstack(self, editorstack):
         for finfo in editorstack.data:
             self.register_widget_shortcuts(finfo.editor)
 
-    @Slot(str, int)
-    def close_file_in_all_editorstacks(self, editorstack_id_str, index):
+    @Slot(str, str)
+    def close_file_in_all_editorstacks(self, editorstack_id_str, filename):
         for editorstack in self.editorstacks:
             if str(id(editorstack)) != editorstack_id_str:
                 editorstack.blockSignals(True)
+                index = editorstack.get_index_from_filename(filename)
                 editorstack.close_file(index, force=True)
                 editorstack.blockSignals(False)
 
@@ -2683,23 +2683,3 @@ def set_create_new_file_if_empty(self, value):
         """Change the value of create_new_file_if_empty"""
         for editorstack in self.editorstacks:
             editorstack.create_new_file_if_empty = value
-
-    @Slot(int, int)
-    def move_editorstack_data(self, start, end):
-        """Move editorstack.data to be synchronized when tabs are moved."""
-        if start < 0 or end < 0:
-            return
-        else:
-            steps = abs(end - start)
-            direction = (end-start) // steps  # +1 for right, -1 for left
-
-            for editorstack in self.editorstacks:
-                if editorstack.isAncestorOf(self.sender()):
-                    data = editorstack.data
-                    editorstack.blockSignals(True)
-
-                    for i in range(start, end, direction):
-                        data[i], data[i+direction] = data[i+direction], data[i]
-
-                    editorstack.blockSignals(False)
-                    editorstack.refresh()
diff --git a/spyder/widgets/editor.py b/spyder/widgets/editor.py
--- a/spyder/widgets/editor.py
+++ b/spyder/widgets/editor.py
@@ -439,7 +439,7 @@ class EditorStack(QWidget):
     zoom_in = Signal()
     zoom_out = Signal()
     zoom_reset = Signal()
-    sig_close_file = Signal(str, int)
+    sig_close_file = Signal(str, str)
     file_saved = Signal(str, int, str)
     file_renamed_in_data = Signal(str, int, str)
     create_new_window = Signal()
@@ -748,6 +748,7 @@ def setup_editorstack(self, parent, layout):
                              corner_widgets=corner_widgets)
         self.tabs.tabBar().setObjectName('plugin-tab')
         self.tabs.set_close_function(self.close_file)
+        self.tabs.tabBar().tabMoved.connect(self.move_editorstack_data)
         self.tabs.setMovable(True)
 
         self.stack_history.refresh()
@@ -1317,8 +1318,7 @@ def update_actions(self):
         self.horsplit_action.setEnabled(state)
         self.versplit_action.setEnabled(state)
 
-
-    #------ Accessors
+    # ------ Accessors
     def get_current_filename(self):
         if self.data:
             return self.data[self.get_stack_index()].filename
@@ -1345,6 +1345,33 @@ def is_file_opened(self, filename=None):
         else:
             return self.has_filename(filename)
 
+    def get_index_from_filename(self, filename):
+        """
+        Return the position index of a file in the tab bar of the editorstack
+        from its name.
+        """
+        filenames = [d.filename for d in self.data]
+        return filenames.index(filename)
+
+    @Slot(int, int)
+    def move_editorstack_data(self, start, end):
+        """Reorder editorstack.data so it is synchronized with the tab bar when
+        tabs are moved."""
+        if start < 0 or end < 0:
+            return
+        else:
+            steps = abs(end - start)
+            direction = (end-start) // steps  # +1 for right, -1 for left
+
+        data = self.data
+        self.blockSignals(True)
+
+        for i in range(start, end, direction):
+            data[i], data[i+direction] = data[i+direction], data[i]
+
+        self.blockSignals(False)
+        self.refresh()
+
 
     #------ Close file, tabwidget...
     def close_file(self, index=None, force=False):
@@ -1376,13 +1403,14 @@ def close_file(self, index=None, force=False):
             if self.outlineexplorer is not None:
                 self.outlineexplorer.remove_editor(finfo.editor)
 
+            filename = self.data[index].filename
             self.remove_from_data(index)
 
             # We pass self object ID as a QString, because otherwise it would
             # depend on the platform: long for 64bit, int for 32bit. Replacing
             # by long all the time is not working on some 32bit platforms
             # (see Issue 1094, Issue 1098)
-            self.sig_close_file.emit(str(id(self)), index)
+            self.sig_close_file.emit(str(id(self)), filename)
 
             if not self.data and self.is_closable:
                 # editortabwidget is empty: removing it
@@ -2741,11 +2769,12 @@ def unregister_editorwindow(self, window):
     def get_focus_widget(self):
         pass
 
-    @Slot(str, int)
-    def close_file_in_all_editorstacks(self, editorstack_id_str, index):
+    @Slot(str, str)
+    def close_file_in_all_editorstacks(self, editorstack_id_str, filename):
         for editorstack in self.editorstacks:
             if str(id(editorstack)) != editorstack_id_str:
                 editorstack.blockSignals(True)
+                index = editorstack.get_index_from_filename(filename)
                 editorstack.close_file(index, force=True)
                 editorstack.blockSignals(False)
 


Test patch: