From 7c4c9ecf88e3436252c670125a7126aa2de33310 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sun, 22 Mar 2026 00:35:51 -0500 Subject: [PATCH] Refactor file loading to detect external modifications and add reload capability - Extract _load_data() method from load_path() for reuse in reload() - Add is_extters externally_modified() to track file state changes (mtime/size) - Replace load_bytes() with load_contents() for better error handling - Add reload() method to re-read file from disk - Fix file_state_watcher by properly detecting external changes --- .../file_state_watcher/plugin.py | 20 ++++-- .../file_state_watcher/watcher_checks.py | 46 ++++++++++---- .../prettify_json/prettify_json.py | 4 +- src/core/widgets/code/source_file.py | 62 +++++++++++++------ 4 files changed, 91 insertions(+), 41 deletions(-) diff --git a/plugins/code/event-watchers/file_state_watcher/plugin.py b/plugins/code/event-watchers/file_state_watcher/plugin.py index 5603f2f..ae17dd6 100644 --- a/plugins/code/event-watchers/file_state_watcher/plugin.py +++ b/plugins/code/event-watchers/file_state_watcher/plugin.py @@ -17,13 +17,21 @@ class Plugin(PluginCode): def _controller_message(self, event: Code_Event_Types.CodeEvent): - if isinstance(event, Code_Event_Types.TextChangedEvent): - event.file.check_file_on_disk() + if not isinstance(event, Code_Event_Types.FocusedViewEvent): return + event = Event_Factory.create_event( + "get_file", buffer = event.view.get_buffer() + ) + self.emit_to("files", event) - if event.file.is_deleted(): - file_is_deleted(event, self.emit) - elif event.file.is_externally_modified(): - file_is_externally_modified(event, self.emit) + file = event.response + if file.ftype == "buffer": return + + file.check_file_on_disk() + + if file.is_deleted(): + file_is_deleted(file, self.emit) + elif file.is_externally_modified(): + file_is_externally_modified(file, self.emit) def load(self): ... diff --git a/plugins/code/event-watchers/file_state_watcher/watcher_checks.py b/plugins/code/event-watchers/file_state_watcher/watcher_checks.py index 51dfa78..f3d4f02 100644 --- a/plugins/code/event-watchers/file_state_watcher/watcher_checks.py +++ b/plugins/code/event-watchers/file_state_watcher/watcher_checks.py @@ -10,23 +10,45 @@ from libs.event_factory import Event_Factory, Code_Event_Types -def file_is_deleted(event, emit): - event.file.was_deleted = True +def ask_yes_no(message): + dialog = Gtk.MessageDialog( + parent = None, + flags = 0, + message_type = Gtk.MessageType.QUESTION, + buttons = Gtk.ButtonsType.YES_NO, + text = message, + ) + dialog.set_title("Confirm") + + response = dialog.run() + dialog.destroy() + + return response == Gtk.ResponseType.YES + + +def file_is_deleted(file, emit): + file.was_deleted = True event = Event_Factory.create_event( "file_externally_deleted", - file = event.file, - buffer = event.buffer + file = file, + buffer = file.buffer ) emit(event) -def file_is_externally_modified(event, emit): -# event = Event_Factory.create_event( -# "file_externally_modified", -# file = event.file, -# buffer = event.buffer -# ) -# emit(event) +def file_is_externally_modified(file, emit): + event = Event_Factory.create_event( + "file_externally_modified", + file = file, + buffer = file.buffer + ) + emit(event) - ... + if not file.buffer.get_modified(): + file.reload() + return + result = ask_yes_no("File has been externally modified. Reload?") + if not result: return + + file.reload() diff --git a/plugins/code/event-watchers/prettify_json/prettify_json.py b/plugins/code/event-watchers/prettify_json/prettify_json.py index ad03e62..ae32086 100644 --- a/plugins/code/event-watchers/prettify_json/prettify_json.py +++ b/plugins/code/event-watchers/prettify_json/prettify_json.py @@ -13,8 +13,6 @@ from gi.repository import Gtk def add_prettify_json(buffer, menu): - menu.append(separator) - def on_prettify_json(menuitem, buffer): start_itr, \ end_itr = buffer.get_start_iter(), buffer.get_end_iter() @@ -28,4 +26,4 @@ def add_prettify_json(buffer, menu): item = Gtk.MenuItem(label = "Prettify JSON") item.connect("activate", on_prettify_json, buffer) - menu.append(item) + menu.append(item) \ No newline at end of file diff --git a/src/core/widgets/code/source_file.py b/src/core/widgets/code/source_file.py index c21b8cf..1e3efc2 100644 --- a/src/core/widgets/code/source_file.py +++ b/src/core/widgets/code/source_file.py @@ -120,21 +120,7 @@ class SourceFile(GtkSource.File): return gfile - - def load_path(self, gfile: Gio.File): - if not gfile: return - - text = gfile.load_bytes()[0].get_data().decode("UTF-8") - info = gfile.query_info('standard::content-type', Gio.FileQueryInfoFlags.NONE, None) - content_type = info.get_content_type() - self.ftype = Gio.content_type_get_mime_type(content_type) \ - .replace("application/", "") \ - .replace("text/", "") \ - .replace("x-", "") - - self.set_path(gfile) - logger.debug(f"File content type: {self.ftype}") - + def _load_data(self, text: str, is_new: bool = True): undo_manager = self.buffer.get_undo_manager() self.buffer.block_changed_signal() @@ -151,21 +137,49 @@ class SourceFile(GtkSource.File): self.buffer.delete(start_itr, end_itr) self.buffer.insert(start_itr, text, -1) + self.is_externally_modified() GLib.idle_add(move_insert_to_start) undo_manager.end_not_undoable_action() self.buffer.set_modified(False) - eve = Event_Factory.create_event( - "loaded_new_file", - file = self - ) - self.emit(eve) + if is_new: + eve = Event_Factory.create_event( + "loaded_new_file", + file = self + ) + self.emit(eve) self.buffer.unblock_changed_signal() self.buffer.unblock_changed_after_signal() self.buffer.unblock_modified_changed_signal() + def is_externally_modified(self) -> bool: + stat = os.stat(self.fpath) + current = (stat.st_mtime_ns, stat.st_size) + + is_modified = \ + hasattr(self, "last_state") and not current == self.last_state + + self.last_state = current + return is_modified + + def load_path(self, gfile: Gio.File): + if not gfile: return + loaded, contents, etag_out = gfile.load_contents() + if not loaded: raise Exception("File couldn't be loaded...'") + + text = contents.decode("UTF-8") + info = gfile.query_info('standard::content-type', Gio.FileQueryInfoFlags.NONE, None) + content_type = info.get_content_type() + self.ftype = Gio.content_type_get_mime_type(content_type) \ + .replace("application/", "") \ + .replace("text/", "") \ + .replace("x-", "") + + self.set_path(gfile) + logger.debug(f"File content type: {self.ftype}") + self._load_data(text) def set_path(self, gfile: Gio.File): if not gfile: return @@ -177,9 +191,17 @@ class SourceFile(GtkSource.File): event = Event_Factory.create_event("file_path_set", file = self) self.emit(event) + def reload(self): + loaded, contents, etag_out = self.get_location().load_contents() + if not loaded: raise Exception("File couldn't be re-loaded...'") + + text = contents.decode("UTF-8") + self._load_data(text, False) + def save(self): self._write_file( self.get_location() ) + self.is_externally_modified() self.buffer.set_modified(False) event = Event_Factory.create_event( "saved_file",