Skip to content

Commit

Permalink
Merge pull request #5784 from csabella/issue5703
Browse files Browse the repository at this point in the history
PR: Change for file save on split editor when tabs are moved
  • Loading branch information
ccordoba12 authored Nov 19, 2017
2 parents b09239f + 21e046f commit 44ddc68
Show file tree
Hide file tree
Showing 3 changed files with 473 additions and 41 deletions.
22 changes: 13 additions & 9 deletions spyder/plugins/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1364,20 +1364,22 @@ def close_file_in_all_editorstacks(self, editorstack_id_str, filename):
editorstack.close_file(index, force=True)
editorstack.blockSignals(False)

@Slot(str, int, str)
def file_saved_in_editorstack(self, editorstack_id_str, index, filename):
@Slot(str, str, str)
def file_saved_in_editorstack(self, editorstack_id_str,
original_filename, filename):
"""A file was saved in editorstack, this notifies others"""
for editorstack in self.editorstacks:
if str(id(editorstack)) != editorstack_id_str:
editorstack.file_saved_in_other_editorstack(index, filename)
editorstack.file_saved_in_other_editorstack(original_filename,
filename)

@Slot(str, int, str)
@Slot(str, str, str)
def file_renamed_in_data_in_editorstack(self, editorstack_id_str,
index, filename):
original_filename, filename):
"""A file was renamed in data in editorstack, this notifies others"""
for editorstack in self.editorstacks:
if str(id(editorstack)) != editorstack_id_str:
editorstack.rename_in_data(index, filename)
editorstack.rename_in_data(original_filename, filename)

def set_editorstack_for_introspection(self):
"""
Expand Down Expand Up @@ -1553,9 +1555,10 @@ def analysis_results_changed(self):
results = editorstack.get_analysis_results()
index = editorstack.get_stack_index()
if index != -1:
filename = editorstack.data[index].filename
for other_editorstack in self.editorstacks:
if other_editorstack is not editorstack:
other_editorstack.set_analysis_results(index, results)
other_editorstack.set_analysis_results(filename, results)
self.update_code_analysis_actions()

def update_todo_menu(self):
Expand Down Expand Up @@ -1584,9 +1587,10 @@ def todo_results_changed(self):
results = editorstack.get_todo_results()
index = editorstack.get_stack_index()
if index != -1:
filename = editorstack.data[index].filename
for other_editorstack in self.editorstacks:
if other_editorstack is not editorstack:
other_editorstack.set_todo_results(index, results)
other_editorstack.set_todo_results(filename, results)
self.update_todo_actions()

def refresh_eol_chars(self, os_name):
Expand Down Expand Up @@ -2072,7 +2076,7 @@ def renamed(self, source, dest):
index = self.editorstacks[0].has_filename(filename)
if index is not None:
for editorstack in self.editorstacks:
editorstack.rename_in_data(index,
editorstack.rename_in_data(filename,
new_filename=to_text_string(dest))


Expand Down
161 changes: 129 additions & 32 deletions spyder/widgets/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,8 @@ class EditorStack(QWidget):
zoom_out = Signal()
zoom_reset = Signal()
sig_close_file = Signal(str, str)
file_saved = Signal(str, int, str)
file_renamed_in_data = Signal(str, int, str)
file_saved = Signal(str, str, str)
file_renamed_in_data = Signal(str, str, str)
create_new_window = Signal()
opened_files_list_changed = Signal()
analysis_results_changed = Signal()
Expand Down Expand Up @@ -1231,7 +1231,10 @@ def __repopulate_stack(self):
self.tabs.setTabToolTip(index, tab_tip)
self.tabs.blockSignals(False)

def rename_in_data(self, index, new_filename):
def rename_in_data(self, original_filename, new_filename):
index = self.has_filename(original_filename)
if index is None:
return
finfo = self.data[index]
if osp.splitext(finfo.filename)[1] != osp.splitext(new_filename)[1]:
# File type has changed!
Expand Down Expand Up @@ -1491,7 +1494,21 @@ def set_last_closed_files(self, fnames):

#------ Save
def save_if_changed(self, cancelable=False, index=None):
"""Ask user to save file if modified"""
"""Ask user to save file if modified.
Args:
cancelable: Show Cancel button.
index: File to check for modification.
Returns:
False when save() fails or is cancelled.
True when save() is successful, there are no modifications,
or user selects No or NoToAll.
This function controls the message box prompt for saving
changed files. The actual save is performed in save() for
each index processed.
"""
if index is None:
indexes = list(range(self.get_stack_count()))
else:
Expand All @@ -1513,7 +1530,7 @@ def save_if_changed(self, cancelable=False, index=None):
self.set_stack_index(index)
finfo = self.data[index]
if finfo.filename == self.tempfile_path or yes_all:
if not self.save():
if not self.save(index):
return False
elif finfo.editor.document().isModified():

Expand All @@ -1528,10 +1545,10 @@ def save_if_changed(self, cancelable=False, index=None):

answer = self.msgbox.exec_()
if answer == QMessageBox.Yes:
if not self.save():
if not self.save(index):
return False
elif answer == QMessageBox.YesAll:
if not self.save():
if not self.save(index):
return False
yes_all = True
elif answer == QMessageBox.NoAll:
Expand All @@ -1541,7 +1558,22 @@ def save_if_changed(self, cancelable=False, index=None):
return True

def save(self, index=None, force=False):
"""Save file"""
"""Write text of editor to a file.
Args:
index: self.data index to save. If None, defaults to
currentIndex().
force: Force save regardless of file state.
Returns:
True upon successful save or when file doesn't need to be saved.
False if save failed.
If the text isn't modified and it's not newly created, then the save
is aborted. If the file hasn't been saved before, then save_as()
is invoked. Otherwise, the file is written using the file name
currently in self.data. This function doesn't change the file name.
"""
if index is None:
# Save the currently edited file
if not self.get_stack_count():
Expand Down Expand Up @@ -1569,7 +1601,10 @@ def save(self, index=None, force=False):
# 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.file_saved.emit(str(id(self)), index, finfo.filename)
# The filename is passed instead of an index in case the tabs
# have been rearranged (see issue 5703).
self.file_saved.emit(str(id(self)),
finfo.filename, finfo.filename)

finfo.editor.document().setModified(False)
self.modification_changed(index=index)
Expand Down Expand Up @@ -1597,20 +1632,35 @@ def save(self, index=None, force=False):
self.msgbox.exec_()
return False

def file_saved_in_other_editorstack(self, index, filename):
def file_saved_in_other_editorstack(self, original_filename, filename):
"""
File was just saved in another editorstack, let's synchronize!
This avoid file to be automatically reloaded
This avoids file being automatically reloaded.
Filename is passed in case file was just saved as another name
The original filename is passed instead of an index in case the tabs
on the editor stacks were moved and are now in a different order - see
issue 5703.
Filename is passed in case file was just saved as another name.
"""
index = self.has_filename(original_filename)
if index is None:
return
finfo = self.data[index]
finfo.newly_created = False
finfo.filename = to_text_string(filename)
finfo.lastmodified = QFileInfo(finfo.filename).lastModified()

def select_savename(self, original_filename):
"""Select a name to save a file."""
"""Select a name to save a file.
Args:
original_filename: Used in the dialog to display the current file
path and name.
Returns:
Normalized path for the selected file name or None if no name was
selected.
"""
if self.edit_filetypes is None:
self.edit_filetypes = get_edit_filetypes()
if self.edit_filters is None:
Expand All @@ -1636,9 +1686,28 @@ def select_savename(self, original_filename):
self.redirect_stdio.emit(True)
if filename:
return osp.normpath(filename)
return None

def save_as(self, index=None):
"""Save file as..."""
"""Save file as...
Args:
index: self.data index for the file to save.
Returns:
False if no file name was selected or if save() was unsuccessful.
True is save() was successful.
Gets the new file name from select_savename(). If no name is chosen,
then the save_as() aborts. Otherwise, the current stack is checked
to see if the selected name already exists and, if so, then the tab
with that name is closed.
The current stack (self.data) and current tabs are updated with the
new name and other file info. The text is written with the new
name using save() and the name change is propagated to the other stacks
via the file_renamed_in_data signal.
"""
if index is None:
# Save the currently edited file
index = self.get_stack_index()
Expand All @@ -1647,7 +1716,8 @@ def save_as(self, index=None):
# While running __check_file_status
# See issues 3678 and 3026
finfo.newly_created = True
filename = self.select_savename(finfo.filename)
original_filename = finfo.filename
filename = self.select_savename(original_filename)
if filename:
ao_index = self.has_filename(filename)
# Note: ao_index == index --> saving an untitled file
Expand All @@ -1657,13 +1727,15 @@ def save_as(self, index=None):
if ao_index < index:
index -= 1

new_index = self.rename_in_data(index, new_filename=filename)
new_index = self.rename_in_data(original_filename,
new_filename=filename)

# 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.file_renamed_in_data.emit(str(id(self)), index, filename)
self.file_renamed_in_data.emit(str(id(self)),
original_filename, filename)

ok = self.save(index=new_index, force=True)
self.refresh(new_index)
Expand All @@ -1673,12 +1745,30 @@ def save_as(self, index=None):
return False

def save_copy_as(self, index=None):
"""Save copy of file as..."""
"""Save copy of file as...
Args:
index: self.data index for the file to save.
Returns:
False if no file name was selected or if save() was unsuccessful.
True is save() was successful.
Gets the new file name from select_savename(). If no name is chosen,
then the save_copy_as() aborts. Otherwise, the current stack is
checked to see if the selected name already exists and, if so, then the
tab with that name is closed.
Unlike save_as(), this calls write() directly instead of using save().
The current file and tab aren't changed at all. The copied file is
opened in a new tab.
"""
if index is None:
# Save the currently edited file
index = self.get_stack_index()
finfo = self.data[index]
filename = self.select_savename(finfo.filename)
original_filename = finfo.filename
filename = self.select_savename(original_filename)
if filename:
ao_index = self.has_filename(filename)
# Note: ao_index == index --> saving an untitled file
Expand All @@ -1690,8 +1780,6 @@ def save_copy_as(self, index=None):
txt = to_text_string(finfo.editor.get_text_with_eol())
try:
finfo.encoding = encoding.write(txt, filename, finfo.encoding)
self.file_saved.emit(str(id(self)), index, filename)

# open created copy file
self.plugin_load.emit(filename)
return True
Expand All @@ -1709,11 +1797,12 @@ def save_copy_as(self, index=None):
return False

def save_all(self):
"""Save all opened files"""
folders = set()
"""Save all opened files.
Iterate through self.data and call save() on any modified files.
"""
for index in range(self.get_stack_count()):
if self.data[index].editor.document().isModified():
folders.add(osp.dirname(self.data[index].filename))
self.save(index)

#------ Update UI
Expand All @@ -1738,16 +1827,22 @@ def analyze_script(self, index=None):
finfo.run_todo_finder()
self.is_analysis_done = True

def set_analysis_results(self, index, analysis_results):
def set_analysis_results(self, filename, analysis_results):
"""Synchronize analysis results between editorstacks"""
index = self.has_filename(filename)
if index is None:
return
self.data[index].set_analysis_results(analysis_results)

def get_analysis_results(self):
if self.data:
return self.data[self.get_stack_index()].analysis_results

def set_todo_results(self, index, todo_results):
def set_todo_results(self, filename, todo_results):
"""Synchronize todo results between editorstacks"""
index = self.has_filename(filename)
if index is None:
return
self.data[index].set_todo_results(todo_results)

def get_todo_results(self):
Expand Down Expand Up @@ -2800,22 +2895,24 @@ def close_file_in_all_editorstacks(self, editorstack_id_str, filename):

# This method is never called in this plugin example. It's here only
# to show how to use the file_saved signal (see above).
@Slot(str, int, str)
def file_saved_in_editorstack(self, editorstack_id_str, index, filename):
@Slot(str, str, str)
def file_saved_in_editorstack(self, editorstack_id_str,
original_filename, filename):
"""A file was saved in editorstack, this notifies others"""
for editorstack in self.editorstacks:
if str(id(editorstack)) != editorstack_id_str:
editorstack.file_saved_in_other_editorstack(index, filename)
editorstack.file_saved_in_other_editorstack(original_filename,
filename)

# This method is never called in this plugin example. It's here only
# to show how to use the file_saved signal (see above).
@Slot(str, int, str)
@Slot(str, str, str)
def file_renamed_in_data_in_editorstack(self, editorstack_id_str,
index, filename):
original_filename, filename):
"""A file was renamed in data in editorstack, this notifies others"""
for editorstack in self.editorstacks:
if str(id(editorstack)) != editorstack_id_str:
editorstack.rename_in_data(index, filename)
editorstack.rename_in_data(original_filename, filename)

def register_widget_shortcuts(self, widget):
"""Fake!"""
Expand Down
Loading

0 comments on commit 44ddc68

Please sign in to comment.