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:
@@ -3,5 +3,6 @@
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"autoload": false,
|
||||
"requests": {}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,11 @@ class Plugin(PluginCode):
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_lsp_client",
|
||||
lang_id = "java"
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -40,7 +40,7 @@ class LSPManager(ControllerBase):
|
||||
self.lsp_manager_ui.connect('close-client', self._on_close_client)
|
||||
|
||||
def _do_bind_mapping(self):
|
||||
self.response_cache.set_lsp_client(self.lsp_manager_client)
|
||||
self.response_cache.set_lsp_manager_client(self.lsp_manager_client)
|
||||
self.provider.response_cache = self.response_cache
|
||||
self.response_registry.set_event_hub(
|
||||
self.emit, self.emit_to, self.provider
|
||||
@@ -52,6 +52,7 @@ class LSPManager(ControllerBase):
|
||||
self.lsp_manager_ui.add_client_listing(event.lang_id, event.lang_config)
|
||||
elif isinstance(event, Code_Event_Types.UnregisterLspClientEvent):
|
||||
self.response_registry.unregister_handler(event.lang_id)
|
||||
self.lsp_manager_ui.remove_client_listing(event.lang_id)
|
||||
|
||||
def _on_create_client(self, ui, lang_id: str, workspace_uri: str) -> bool:
|
||||
init_opts = ui.get_init_opts(lang_id)
|
||||
@@ -66,6 +67,10 @@ class LSPManager(ControllerBase):
|
||||
ui.toggle_client_buttons(show_close=False)
|
||||
return result
|
||||
|
||||
def handle_destroy(self):
|
||||
self.lsp_manager_ui.disconnect_by_func(self._on_create_client)
|
||||
self.lsp_manager_ui.disconnect_by_func(self._on_close_client)
|
||||
|
||||
def create_client(
|
||||
self,
|
||||
lang_id: str = "python",
|
||||
|
||||
@@ -41,7 +41,8 @@ class LSPManagerUI(Gtk.Dialog):
|
||||
self.set_hexpand(True)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("show", self._show)
|
||||
self.connect("show", self._handle_show)
|
||||
self.connect("destroy", self._handle_destroy)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
@@ -68,7 +69,7 @@ class LSPManagerUI(Gtk.Dialog):
|
||||
|
||||
self.path_bttn.connect("file-set", self._file_set)
|
||||
self.combo_box.connect("changed", self._on_combo_changed)
|
||||
self.hide_bttn.connect("clicked", lambda widget: self.hide())
|
||||
self.hide_bttn_id = self.hide_bttn.connect("clicked", lambda widget: self.hide())
|
||||
self.create_client_bttn.connect("clicked", self._create_client, self.close_client_bttn)
|
||||
self.close_client_bttn.connect("clicked", self._close_client, self.create_client_bttn)
|
||||
|
||||
@@ -92,9 +93,18 @@ class LSPManagerUI(Gtk.Dialog):
|
||||
self.close_client_bttn.hide()
|
||||
bttn_box.hide()
|
||||
|
||||
def _show(self, widget):
|
||||
def _handle_show(self, widget):
|
||||
GLib.idle_add(self.path_entry.grab_focus)
|
||||
|
||||
def _handle_destroy(self, widget):
|
||||
self.disconnect_by_func(self._show)
|
||||
self.disconnect_by_func(self._handle_destroy)
|
||||
self.path_bttn.disconnect_by_func(self._file_set)
|
||||
self.combo_box.disconnect_by_func(self._on_combo_changed)
|
||||
self.hide_bttn.disconnect(self.hide_bttn_id)
|
||||
self.create_client_bttn.disconnect_by_func(self._create_client)
|
||||
self.close_client_bttn.disconnect_by_func(self._close_client)
|
||||
|
||||
def _map_resize(self, widget, parent):
|
||||
parent_x, parent_y = parent.get_position()
|
||||
parent_width, parent_height = parent.get_size()
|
||||
@@ -163,7 +173,10 @@ class LSPManagerUI(Gtk.Dialog):
|
||||
buffer.set_text(json_str, -1)
|
||||
|
||||
def map_parent_resize_event(self, parent):
|
||||
parent.connect("size-allocate", lambda w, r: self._map_resize(self, parent))
|
||||
self.size_allocate_id = parent.connect("size-allocate", lambda w, r: self._map_resize(self, parent))
|
||||
|
||||
def unmap_parent_resize_event(self, parent):
|
||||
parent.disconnect(self.size_allocate_id)
|
||||
|
||||
def set_source_view(self, source_view):
|
||||
scrolled_win = Gtk.ScrolledWindow()
|
||||
@@ -187,6 +200,17 @@ class LSPManagerUI(Gtk.Dialog):
|
||||
self.combo_box.append_text(lang_id)
|
||||
self.client_configs[lang_id] = lang_config
|
||||
|
||||
def remove_client_listing(self, lang_id: str):
|
||||
model = self.combo_box.get_model()
|
||||
|
||||
for i, row in enumerate(model):
|
||||
if row[0] == lang_id: # assuming text is in column 0
|
||||
self.combo_box.remove(i)
|
||||
break
|
||||
|
||||
if lang_id in self.client_configs:
|
||||
del self.client_configs[lang_id]
|
||||
|
||||
def get_init_opts(self, lang_id: str) -> dict:
|
||||
if not lang_id or lang_id not in self.client_configs: return {}
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"pre_launch": true,
|
||||
"autoload": false,
|
||||
"requests": {}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,31 @@ class Plugin(PluginCode):
|
||||
source_view = event.response
|
||||
lsp_manager.lsp_manager_ui.set_source_view(source_view)
|
||||
|
||||
def unload(self):
|
||||
Event_Factory.unregister_events( lsp_events.__dict__.items() )
|
||||
|
||||
self.unregister_controller("lsp_manager")
|
||||
|
||||
window = self.request_ui_element("main-window")
|
||||
|
||||
lsp_manager.lsp_manager_ui.unmap_parent_resize_event(window)
|
||||
|
||||
event = Event_Factory.create_event("unregister_command",
|
||||
command_name = "LSP Manager",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = ["<Shift><Control>l", "<Control>g", "<Control>i"]
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "LSP Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
lsp_manager.handle_destroy()
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
@@ -15,27 +15,27 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
self._lsp_client = None
|
||||
self.matchers: dict = {}
|
||||
self.lsp_manager_client = None
|
||||
|
||||
def set_lsp_client(self, lsp_client):
|
||||
self._lsp_client = lsp_client
|
||||
def set_lsp_manager_client(self, lsp_client):
|
||||
self.lsp_manager_client = lsp_client
|
||||
|
||||
def process_file_load(self, event):
|
||||
if self._lsp_client:
|
||||
self._lsp_client.process_file_load(event)
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_load(event)
|
||||
|
||||
def process_file_close(self, event):
|
||||
if self._lsp_client:
|
||||
self._lsp_client.process_file_close(event)
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_close(event)
|
||||
|
||||
def process_file_save(self, event):
|
||||
if self._lsp_client:
|
||||
self._lsp_client.process_file_save(event)
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_save(event)
|
||||
|
||||
def process_file_change(self, event):
|
||||
if self._lsp_client:
|
||||
self._lsp_client.process_file_change(event)
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_change(event)
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
return []
|
||||
|
||||
@@ -33,7 +33,7 @@ class ResponseRegistry:
|
||||
def register_handler(self, lang_id: str, handler_cls: type[BaseHandler]):
|
||||
self._lang_handlers[lang_id] = handler_cls
|
||||
|
||||
def unregister_handler(self, lang_id: str, handler_cls: type[BaseHandler]):
|
||||
def unregister_handler(self, lang_id: str):
|
||||
del self._lang_handlers[lang_id]
|
||||
|
||||
def get_handler(self, lang_id: str = "", method: str = ""):
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"autoload": false,
|
||||
"requests": {}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,11 @@ class Plugin(PluginCode):
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_lsp_client",
|
||||
lang_id = "python"
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
Reference in New Issue
Block a user