feat: Complete plugin lifecycle management with lazy loading and runtime reload

Major changes:
- Add unload() method to all plugins for proper cleanup (unregister commands/providers/LSP clients, destroy widgets, clear state)
- Implement lazy widget loading via "show" signal across all containers
- Add autoload: false manifest option for manual/conditional plugin loading
- Add Plugins UI with runtime load/unload toggle via Ctrl+Shift+p
- Implement controller unregistration system with proper signal disconnection
- Add new events: UnregisterCommandEvent, GetFilesEvent, GetSourceViewsEvent, TogglePluginsUiEvent
- Fix signal leaks by tracking and disconnecting handlers in widgets (search/replace, LSP manager, tabs, telescope, markdown preview)
- Add Save/Save As to tabs context menu
- Improve search/replace behavior (selection handling, focus management)
- Add telescope file initialization from existing loaded files
- Refactor plugin reload watcher to dynamically add/remove plugins on filesystem changes
- Add new plugins: file_history, extend_source_view_menu, godot_lsp_client
- Fix bug in prettify_json (undefined variable reference
This commit is contained in:
2026-03-21 13:26:12 -05:00
parent 0fc440e7ce
commit 0b231ac749
73 changed files with 1157 additions and 222 deletions

View File

@@ -20,13 +20,27 @@ class Plugin(PluginCode):
...
def load(self):
tabs_controller = TabsController()
self.tabs_controller = TabsController()
code_container = self.request_ui_element("code-container")
self.register_controller("tabs", tabs_controller)
self.register_controller("tabs", self.tabs_controller)
code_container.add( tabs_controller.tabs_widget )
code_container.reorder_child(tabs_controller.tabs_widget, 0)
code_container.add( self.tabs_controller.tabs_widget )
code_container.reorder_child(self.tabs_controller.tabs_widget, 0)
event = Event_Factory.create_event("get_files")
self.emit_to("files", event)
for file in event.response:
self.tabs_controller.add_tab(file)
def unload(self):
self.unregister_controller("tabs")
self.tabs_controller.unload_tabs()
self.tabs_controller.tabs_widget.destroy()
self.tabs_controller.tabs_widget = None
self.tabs_controller = None
del self.tabs_controller
def run(self):
...

View File

@@ -32,7 +32,6 @@ class TabWidget(Gtk.Box):
self.set_orientation(0)
self.set_hexpand(False)
self.set_vexpand(False)
self.set_can_focus(False)
self.set_size_request(-1, 12)
def _setup_signals(self):
@@ -44,10 +43,6 @@ class TabWidget(Gtk.Box):
self.close_bttn = Gtk.Button()
icon = Gtk.Image(stock = Gtk.STOCK_CLOSE)
self.event_box.set_can_focus(False)
self.label.set_can_focus(False)
self.close_bttn.set_can_focus(False)
self.event_box.set_above_child(True)
ctx = self.label.get_style_context()
ctx.add_class("tab-label")

View File

@@ -16,7 +16,6 @@ from .tab_widget import TabWidget
class TabsController(ControllerBase):
def __init__(self):
super(TabsController, self).__init__()
@@ -35,7 +34,7 @@ class TabsController(ControllerBase):
elif isinstance(event, Code_Event_Types.FileExternallyDeletedEvent):
self.tabs_widget.externally_deleted( event.buffer )
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.add_tab(event)
self.add_tab(event.file)
elif isinstance(event, Code_Event_Types.PoppedFileEvent):
...
elif isinstance(event, Code_Event_Types.RemovedFileEvent):
@@ -53,11 +52,11 @@ class TabsController(ControllerBase):
break
def add_tab(self, event: Code_Event_Types.AddedNewFileEvent):
def add_tab(self, file):
tab = TabWidget()
tab.file = event.file
tab.file = file
tab.label.set_label(event.file.fname)
tab.label.set_label(file.fname)
self.tabs_widget.append_page(Gtk.Separator(), tab)
tab.show_all()
@@ -73,3 +72,13 @@ class TabsController(ControllerBase):
)
break
def unload_tabs(self):
for page_widget in self.tabs_widget.get_children():
tab = self.tabs_widget.get_tab_label(page_widget)
tab.clear_signals_and_data()
self.tabs_widget.remove_page(
self.tabs_widget.page_num(page_widget)
)

View File

@@ -33,6 +33,7 @@ class TabsWidget(Gtk.Notebook):
self.connect("page-added", self._page_added)
self.switch_page_id = \
self.connect_after("switch-page", self._switch_page)
self.connect("destroy", self._handle_destroy)
def _subscribe_to_events(self):
...
@@ -81,25 +82,38 @@ class TabsWidget(Gtk.Notebook):
)
def create_menu(self, page_widget) -> Gtk.Menu:
context_menu = Gtk.Menu()
context_menu = Gtk.Menu()
close_submenu = Gtk.Menu()
save_item = Gtk.MenuItem(label = "Save")
save_as_item = Gtk.MenuItem(label = "Save As")
close_item = Gtk.MenuItem(label = "Close Tab")
close_left_item = Gtk.MenuItem(label = "Close Tabs Left")
close_right_item = Gtk.MenuItem(label = "Close Tabs Right")
close_other_item = Gtk.MenuItem(label = "Close Other Tabs")
close_all_item = Gtk.MenuItem(label = "Close All Tabs")
close_actions_menu = Gtk.MenuItem(label = "Close Actions")
close_item = Gtk.MenuItem(label = "Close Tab")
close_left_item = Gtk.MenuItem(label = "Close Tabs Left")
close_right_item = Gtk.MenuItem(label = "Close Tabs Right")
close_other_item = Gtk.MenuItem(label = "Close Other Tabs")
close_all_item = Gtk.MenuItem(label = "Close All Tabs")
close_item.connect("activate", self.close_item, page_widget)
close_left_item.connect("activate", self.close_left_items, page_widget)
save_item.connect("activate", self.save_item, page_widget)
save_as_item.connect("activate", self.save_as_item, page_widget)
close_item.connect("activate", self.close_item, page_widget)
close_left_item.connect("activate", self.close_left_items, page_widget)
close_right_item.connect("activate", self.close_right_items, page_widget)
close_other_item.connect("activate", self.close_other_items, page_widget)
close_all_item.connect("activate", self.close_all_items, page_widget)
close_all_item.connect("activate", self.close_all_items, page_widget)
context_menu.append(close_item)
context_menu.append(close_left_item)
context_menu.append(close_right_item)
context_menu.append(close_other_item)
context_menu.append(close_all_item)
close_submenu.append(close_item)
close_submenu.append(close_left_item)
close_submenu.append(close_right_item)
close_submenu.append(close_other_item)
close_submenu.append(close_all_item)
close_actions_menu.set_submenu(close_submenu)
context_menu.append(save_item)
context_menu.append(save_as_item)
context_menu.append(close_actions_menu)
context_menu.show_all()
@@ -115,7 +129,6 @@ class TabsWidget(Gtk.Notebook):
self.set_current_page(
self.page_num(page_widget)
)
self.handler_unblock(self.switch_page_id)
break
@@ -143,6 +156,14 @@ class TabsWidget(Gtk.Notebook):
break
def save_item(self, menu_item, page_widget):
tab = self.get_tab_label(page_widget)
tab.file.save()
def save_as_item(self, menu_item, page_widget):
tab = self.get_tab_label(page_widget)
tab.file.save_as()
def close_item(self, menu_item, page_widget):
tab = self.get_tab_label(page_widget)
tab.close_bttn.clicked()
@@ -177,3 +198,9 @@ class TabsWidget(Gtk.Notebook):
for widget in children[ : ]:
tab = self.get_tab_label(widget)
tab.close_bttn.clicked()
def _handle_destroy(self, widget):
self.disconnect_by_func(self._page_added)
self.disconnect_by_func(self._switch_page)
self.disconnect_by_func(self._handle_destroy)