Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix save text editor #2667

Merged
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Force Tags settings, after Robot Framework 7.0

=== Fixed

-
- Fixed resource files dissapearing from Project tree on Windows
- Fixed missing indication of link for User Keyword, when pressing ``Ctrl`` in Grid Editor
- Fixed exception when finding GREY color for excluded files and directories in Project Tree
Expand Down
2 changes: 2 additions & 0 deletions src/robotide/application/CHANGELOG.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
</li><li class="listitem">
Added support for JSON variables, by using the installed Robot Framework import method
</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_fixed"></a>1.2. Fixed</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Fixed escaped spaces showing in Text Editor on commented cells
</li><li class="listitem">
Fixed resource files dissapearing from Project tree on Windows
</li><li class="listitem">
Fixed missing indication of link for User Keyword, when pressing ``Ctrl`` in Grid Editor
Expand Down
3 changes: 2 additions & 1 deletion src/robotide/application/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class RIDE(wx.App):
frame = None
namespace = None
preferences = None
robot_version = None
settings = None
treeplugin = None

Expand Down Expand Up @@ -117,10 +118,10 @@ def OnInit(self): # Overrides wx method
if not self.fileexplorerplugin.opened:
self.fileexplorerplugin.close_tree()
self.editor = self._get_editor()
self.robot_version = self._find_robot_installation()
self._load_data()
self.treeplugin.populate(self.model)
self.treeplugin.set_editor(self.editor)
self._find_robot_installation()
self._publish_system_info()
self.frame.Show() # ###### DEBUG DANGER ZONE
self.SetTopWindow(self.frame)
Expand Down
3 changes: 2 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Fixed escaped spaces showing in Text Editor on commented cells</li>
<li>Improved keywords documentation search, by adding current dir to search</li>
<li>Improved Move up/down, <b>Alt-UpArrow</b>/<b>Alt-DownArrow</b> in Text Editor, to have proper indentation and selection</li>
<li>Added auto update check when development version is installed</li>
Expand Down Expand Up @@ -248,6 +249,6 @@ def set_content(self, html_win, content):
<pre class="literal-block">
python -m robotide.postinstall -install
</pre>
<p>RIDE {VERSION} was released on 26/Oct/2023.</p>
<p>RIDE {VERSION} was released on 1/Nov/2023.</p>
</div>
"""
4 changes: 4 additions & 0 deletions src/robotide/controller/filecontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,10 @@ def longname(self):
def suites(self):
return ()

@property
def preamble(self):
return self.data.preamble

def contains_tests(self):
return bool(self.tests)

Expand Down
8 changes: 8 additions & 0 deletions src/robotide/controller/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ def new_resource(self, path, parent=None):
return resource_controller

def load_data(self, path, load_observer=None):
""" DEBUG: To be used in Localization
from robotide.context import APP
try:
robot_version = APP.robot_version
except AttributeError:
robot_version = b'6.1.1' # It is failing at unit tests
print(f"DEBUG: project.py Project ENTER robot version = {robot_version}")
"""
load_observer = load_observer or NullObserver()
if self._load_initfile(path, load_observer):
return
Expand Down
102 changes: 56 additions & 46 deletions src/robotide/editor/texteditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
from ..controller.macrocontrollers import WithStepsController
from ..namespace.suggesters import SuggestionSource
from ..pluginapi import Plugin, action_info_collection, TreeAwarePluginMixin
from ..publish.messages import (RideSaving, RideTreeSelection, RideNotebookTabChanging, RideDataChanged, RideOpenSuite,
RideDataChangedToDirty)
from ..publish.messages import (RideSaved, RideTreeSelection, RideNotebookTabChanging, RideDataChanged, RideOpenSuite,
RideDataChangedToDirty, RideBeforeSaving, RideSaving, RideDataDirtyCleared)
from ..preferences.editors import read_fonts
from ..publish import RideSettingsChanged, PUBLISHER
from ..publish.messages import RideMessage
Expand All @@ -54,6 +54,7 @@ def __init__(self, application):
Plugin.__init__(self, application)
self._editor_component = None
self._tab = None
self._save_flag = 0 # See
self.reformat = application.settings.get('reformat', False)
self._register_shortcuts()

Expand All @@ -70,7 +71,6 @@ def _editor(self):
def enable(self):
self._tab = self._editor
self.register_actions(action_info_collection(_EDIT, self._tab, self._tab))
# DEBUG Disable own saving self.subscribe(self.on_saving, RideSaving)
self.subscribe(self.on_tree_selection, RideTreeSelection)
self.subscribe(self.on_data_changed, RideMessage)
self.subscribe(self.on_tab_change, RideNotebookTabChanging)
Expand All @@ -91,7 +91,6 @@ def f(event):
self.register_shortcut('CtrlCmd-A', focused(lambda e: self._editor.select_all()))
# No system needs this key binding, because is already global
# self.register_shortcut('CtrlCmd-V', focused(lambda e: self._editor.paste()))
# self.register_shortcut('CtrlCmd-S', focused(lambda e: self.on_saving(e)))
self.register_shortcut('CtrlCmd-F', focused(lambda e: self._editor.search_field.SetFocus()))
# To avoid double actions these moved to on_key_down
# self.register_shortcut('CtrlCmd-G', focused(lambda e: self._editor.on_find(e)))
Expand All @@ -116,30 +115,40 @@ def on_open(self, event):
def _open(self):
datafile_controller = self.tree.get_selected_datafile_controller()
if datafile_controller:
self._save_flag = 0
""" DEBUG: To be used in Localization
if hasattr(datafile_controller, 'preamble'): # DEBUG: Is failing at resource files
print(f"DEBUG: texteditor _open preamble={datafile_controller.preamble}")
self._open_data_for_controller(datafile_controller)
"""
self._editor.store_position()

def on_saving(self, message):
_ = message
if self.is_focused():
self._editor.is_saving = False
self._editor.content_save()
elif isinstance(message, RideSaving):
# print(f"DEBUG: textedit OnSaving Open Saved from other {message=} isfocused={self.is_focused()}")
self._open() # Was saved from other Editor

def on_data_changed(self, message):
""" This block is now inside try/except to avoid errors from unit test """
try:
# print(f"DEBUG: textedit OnDataChanged message={message}")
if self._should_process_data_changed_message(message):
if isinstance(message, RideOpenSuite):
if isinstance(message, RideOpenSuite): # Not reached
self._editor.reset()
self._editor.set_editor_caret_position()
if isinstance(message, RideNotebookTabChanging):
return
if self._editor.dirty and not self._apply_txt_changes_to_model():
if isinstance(message, RideNotebookTabChanging): # Not reached
return
if self.is_focused() and self._save_flag == 0 and isinstance(message, RideSaving): # Workaround for remarked dirty with Ctrl-S
self._save_flag = 1
if self.is_focused() and self._save_flag == 1 and isinstance(message, RideDataDirtyCleared):
self._save_flag = 2
if self.is_focused() and self._save_flag == 2 and isinstance(message, RideSaved):
self._save_flag = 3
wx.CallAfter(self._editor.mark_file_dirty, False)
# DEBUG: This is the unwanted chnge after saving but excluded in this block for performance
# if self.is_focused() and self._save_flag == 3 and isinstance(message, RideDataChangedToDirty):
# self._save_flag = 4
# wx.CallAfter(self._editor.mark_file_dirty, False)
if self.is_focused() and isinstance(message, RideBeforeSaving):
self._editor.is_saving = False
# Reset counter for Workaround for remarked dirty with Ctrl-S
self._save_flag = 0
self._apply_txt_changes_to_model()
self._refresh_timer.Start(500, True)
# For performance reasons only run after all the data changes
except AttributeError:
Expand All @@ -152,8 +161,8 @@ def _on_timer(self, event):

@staticmethod
def _should_process_data_changed_message(message):
return isinstance(message, RideDataChanged) and \
not isinstance(message, RideDataChangedToDirty)
return isinstance(message, (RideDataChanged, RideBeforeSaving, RideSaved, RideSaving, RideDataDirtyCleared))\
and not isinstance(message, RideDataChangedToDirty)

def on_tree_selection(self, message):
self._editor.store_position()
Expand Down Expand Up @@ -201,7 +210,7 @@ def on_tab_change(self, message):
if message.newtab == self.title:
self._open()
self._editor.set_editor_caret_position()
self._editor._dirty = 1
# self._editor._dirty = 1
try:
self._set_read_only(self._editor.source_editor.readonly)
except Exception as e: # DEBUG: When using only Text Editor exists error in message topic
Expand All @@ -210,7 +219,7 @@ def on_tab_change(self, message):
self._editor.remove_and_store_state()
self._editor_component.is_saving = False
self._editor_component.content_save()
self._editor._dirty = 0
# self._editor._dirty = 0

def on_tab_changed(self, event):
_ = event
Expand Down Expand Up @@ -388,7 +397,7 @@ def __init__(self, plugin, parent, title, data_validator):
self.reformat = self.source_editor_parent.app.settings.get('reformat', False)
self._create_ui(title)
self._data = None
self._dirty = 1 # 0 is False and 1 is True, when changed on this editor
# self._dirty = 1 # 0 is False and 1 is True, when changed on this editor
self._position = 0 # Start at 0 if first time access
self.restore_start_pos = self._position
self.restore_end_pos = self._position
Expand Down Expand Up @@ -526,8 +535,9 @@ def set_editor_caret_position(self):

@property
def dirty(self):
return self._dirty == 1 and (self._data.wrapper_data.is_dirty or self.source_editor.IsModified())
# return self._dirty == 1 # self.source_editor.IsModified() and self._dirty == 1
# return self._dirty == 1 and (self._data.wrapper_data.is_dirty or self.source_editor.GetModify())
# return self._dirty == 1 # self.source_editor.GetModify() and self._dirty == 1
return self._data.wrapper_data.is_dirty # or self.source_editor.GetModify()

@property
def datafile_controller(self):
Expand Down Expand Up @@ -810,9 +820,9 @@ def write_ident(self):
self.source_editor.WriteText(spaces)

def reset(self):
self._dirty = 0
# self._dirty = 0
if self._data and not self._data.wrapper_data.is_dirty:
self._mark_file_dirty(False)
self.mark_file_dirty(False)

def content_save(self, *args):
_ = args
Expand Down Expand Up @@ -915,7 +925,7 @@ def on_key(self, *args):

def cut(self):
self.source_editor.Cut()
self._mark_file_dirty(self.source_editor.GetModify())
self.mark_file_dirty(self.source_editor.GetModify())

def copy(self):
self.source_editor.Copy()
Expand All @@ -924,20 +934,20 @@ def paste(self):
focus = wx.Window.FindFocus()
if focus == self.source_editor:
self.source_editor.Paste()
self._mark_file_dirty(self.source_editor.GetModify())
self.mark_file_dirty(self.source_editor.GetModify())

def select_all(self):
self.source_editor.SelectAll()

def undo(self):
self.source_editor.Undo()
self.store_position()
self._mark_file_dirty(self.source_editor.GetModify()) # self._dirty == 1 and
self.mark_file_dirty(self.source_editor.GetModify()) # self._dirty == 1 and

def redo(self):
self.source_editor.Redo()
self.store_position()
self._mark_file_dirty(self.source_editor.GetModify())
self.mark_file_dirty(self.source_editor.GetModify())

def remove_and_store_state(self):
if self.source_editor:
Expand Down Expand Up @@ -985,7 +995,7 @@ def on_editor_key(self, event):
return
keycode = event.GetKeyCode()
keyvalue = event.GetUnicodeKey()
self._dirty = 1
# self._dirty = 1
# print(f"DEBUG: TextEditor key up focused={self.is_focused()} modify {self.source_editor.GetModify()}")
if keycode == wx.WXK_DELETE: # DEBUG on Windows we only get here, single Text Editor
selected = self.source_editor.GetSelection()
Expand All @@ -995,14 +1005,14 @@ def on_editor_key(self, event):
self.source_editor.DeleteRange(selected[0], 1)
else:
self.source_editor.DeleteRange(selected[0], selected[1] - selected[0])
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
if keycode in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
return
if keyvalue == wx.WXK_NONE and keycode in [wx.WXK_CONTROL, wx.WXK_RAW_CONTROL]:
self.source_editor.hide_kw_doc()
if self.is_focused(): # DEBUG and keycode != wx.WXK_CONTROL and keyvalue >= ord(' ') and self.dirty:
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
event.Skip()

def on_key_down(self, event):
Expand All @@ -1019,10 +1029,10 @@ def on_key_down(self, event):
return
keycode = event.GetUnicodeKey()
raw_key = event.GetKeyCode()
self._dirty = 1
# self._dirty = 1
# print(f"DEBUG: TextEditor on_key_down event={event} raw_key={raw_key} wx.WXK_C ={wx.WXK_CONTROL}")
if event.GetKeyCode() == wx.WXK_DELETE:
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
return
if raw_key != wx.WXK_CONTROL: # We need to clear doc as soon as possible
self.source_editor.hide_kw_doc()
Expand All @@ -1031,14 +1041,14 @@ def on_key_down(self, event):
self._showing_list = False
wx.CallAfter(self.write_ident) # DEBUG: Make this configurable?
event.Skip()
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
return
selected = self.source_editor.GetSelection()
if selected[0] == selected[1]:
self.write_ident()
else:
self.indent_block()
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
elif event.GetKeyCode() == wx.WXK_TAB and event.ShiftDown():
selected = self.source_editor.GetSelection()
if selected[0] == selected[1]:
Expand All @@ -1050,24 +1060,24 @@ def on_key_down(self, event):
self.source_editor.SetSelection(pos, pos)
else:
self.deindent_block()
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
elif event.GetKeyCode() in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
if not self._showing_list:
self.auto_indent()
else:
self._showing_list = False
wx.CallAfter(self.write_ident) # DEBUG: Make this configurable?
event.Skip()
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
elif keycode in (ord('1'), ord('2'), ord('5')) and event.ControlDown():
self.execute_variable_creator(list_variable=(keycode == ord('2')),
dict_variable=(keycode == ord('5')))
self.store_position()
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
elif (not IS_WINDOWS and not IS_MAC and keycode in (ord('v'), ord('V'))
and event.ControlDown() and not event.ShiftDown()):
# We need to ignore this in Linux, because it does double-action
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
return
elif keycode in (ord('g'), ord('G')) and event.ControlDown():
if event.ShiftDown():
Expand All @@ -1077,7 +1087,7 @@ def on_key_down(self, event):
return
elif keycode in (ord('d'), ord('D')) and event.ControlDown() and not event.ShiftDown():
# We need to ignore because Scintilla does Duplicate line
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
return
elif event.ControlDown() and raw_key == wx.WXK_CONTROL:
# This must be the last branch to activate actions before doc
Expand All @@ -1086,7 +1096,7 @@ def on_key_down(self, event):
event.Skip()
else:
# if self.dirty and keycode >= ord(' '):
self._mark_file_dirty(self.source_editor.IsModified())
self.mark_file_dirty(self.source_editor.GetModify())
event.Skip()

# These commands are duplicated by global actions
Expand Down Expand Up @@ -1782,10 +1792,10 @@ def on_settings_changed(self, message):
if setting == 'reformat':
self.reformat = self.source_editor_parent.app.settings.get('reformat', False)

def _mark_file_dirty(self, dirty=True):
def mark_file_dirty(self, dirty=True):
if not self.is_focused(): # DEBUG: Was marking file clean from Grid Editor
return
if self._data and self._dirty == 1:
if self._data: # and self._dirty == 1:
if dirty:
self._data.mark_data_dirty()
else:
Expand Down
Loading