Remove tabs UI from code editor and move to plugin. Enhance plugin system.
- Remove tabs controller, tab widget, and tabs widget files and move to plugin - Delete plugins/README.txt - Add register_controller method to controller system for plugin use - Add error handling for plugin crashes via futures callback
This commit is contained in:
@@ -1,31 +0,0 @@
|
|||||||
### Note
|
|
||||||
Copy the example and rename it to your desired name. Plugins define a ui target slot with the 'ui_target' requests data but don't have to if not directly interacted with.
|
|
||||||
Plugins must have a run method defined; though, you do not need to necessarily do anything within it. The run method implies that the passed in event system or other data is ready for the plugin to use.
|
|
||||||
|
|
||||||
|
|
||||||
### Manifest Example (All are required even if empty.)
|
|
||||||
```
|
|
||||||
class Manifest:
|
|
||||||
name: str = "Example Plugin"
|
|
||||||
author: str = "John Doe"
|
|
||||||
version: str = "0.0.1"
|
|
||||||
support: str = ""
|
|
||||||
pre_launch: bool = False
|
|
||||||
requests: {} = {
|
|
||||||
'pass_ui_objects': ["plugin_control_list"],
|
|
||||||
'pass_events': True,
|
|
||||||
'bind_keys': []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Requests
|
|
||||||
```
|
|
||||||
requests: {} = {
|
|
||||||
'pass_events': true, # If empty or not present will be ignored.
|
|
||||||
"pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin.
|
|
||||||
'bind_keys': [f"{name}||send_message:<Control>f"],
|
|
||||||
f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
3
plugins/code/tabs_bar/__init__.py
Normal file
3
plugins/code/tabs_bar/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Pligin Module
|
||||||
|
"""
|
||||||
3
plugins/code/tabs_bar/__main__.py
Normal file
3
plugins/code/tabs_bar/__main__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Pligin Package
|
||||||
|
"""
|
||||||
7
plugins/code/tabs_bar/manifest.json
Normal file
7
plugins/code/tabs_bar/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "Tabs Bar",
|
||||||
|
"author": "ITDominator",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"support": "",
|
||||||
|
"requests": {}
|
||||||
|
}
|
||||||
32
plugins/code/tabs_bar/plugin.py
Normal file
32
plugins/code/tabs_bar/plugin.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||||
|
|
||||||
|
from plugins.plugin_types import PluginCode
|
||||||
|
|
||||||
|
from .tabs_controller import TabsController
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(PluginCode):
|
||||||
|
def __init__(self):
|
||||||
|
super(Plugin, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||||
|
...
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
tabs_controller = TabsController()
|
||||||
|
code_container = self.requests_ui_element("code-container")
|
||||||
|
|
||||||
|
self.register_controller("tabs", tabs_controller)
|
||||||
|
|
||||||
|
code_container.add( tabs_controller.tabs_widget )
|
||||||
|
code_container.reorder_child(tabs_controller.tabs_widget, 0)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
...
|
||||||
@@ -9,10 +9,11 @@ from gi.repository import Gtk
|
|||||||
from libs.controllers.controller_base import ControllerBase
|
from libs.controllers.controller_base import ControllerBase
|
||||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||||
|
|
||||||
from ..tabs_widget import TabsWidget
|
from core.widgets.code.source_view import SourceView
|
||||||
from ..tab_widget import TabWidget
|
|
||||||
|
from .tabs_widget import TabsWidget
|
||||||
|
from .tab_widget import TabWidget
|
||||||
|
|
||||||
from ..source_view import SourceView
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +23,8 @@ class TabsWidget(Gtk.Notebook):
|
|||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
self._load_widgets()
|
self._load_widgets()
|
||||||
|
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_scrollable(True)
|
self.set_scrollable(True)
|
||||||
@@ -36,18 +36,15 @@ class CodeContainer(Gtk.Box):
|
|||||||
...
|
...
|
||||||
|
|
||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
|
widget_registery.expose_object("code-container", self)
|
||||||
|
|
||||||
code_base = CodeBase()
|
code_base = CodeBase()
|
||||||
|
|
||||||
self.add( self._create_tabs_widgets(code_base) )
|
|
||||||
self.add( self._create_editor_widget(code_base) )
|
self.add( self._create_editor_widget(code_base) )
|
||||||
|
|
||||||
def _create_tabs_widgets(self, code_base: CodeBase):
|
|
||||||
return code_base.get_tabs_widget()
|
|
||||||
|
|
||||||
def _create_editor_widget(self, code_base: CodeBase):
|
def _create_editor_widget(self, code_base: CodeBase):
|
||||||
editors_container = Gtk.Box()
|
editors_container = Gtk.Box()
|
||||||
|
|
||||||
widget_registery.expose_object("code-container", self)
|
|
||||||
widget_registery.expose_object("editors-container", editors_container)
|
widget_registery.expose_object("editors-container", editors_container)
|
||||||
|
|
||||||
editors_container.add( Separator("separator_left") )
|
editors_container.add( Separator("separator_left") )
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from plugins import plugins_controller
|
|||||||
from libs.controllers.controller_manager import ControllerManager
|
from libs.controllers.controller_manager import ControllerManager
|
||||||
|
|
||||||
from .controllers.files_controller import FilesController
|
from .controllers.files_controller import FilesController
|
||||||
from .controllers.tabs_controller import TabsController
|
|
||||||
from .controllers.commands_controller import CommandsController
|
from .controllers.commands_controller import CommandsController
|
||||||
from .controllers.completion_controller import CompletionController
|
from .controllers.completion_controller import CompletionController
|
||||||
from .controllers.views.source_views_controller import SourceViewsController
|
from .controllers.views.source_views_controller import SourceViewsController
|
||||||
@@ -31,23 +30,18 @@ class CodeBase:
|
|||||||
|
|
||||||
def _load_controllers(self):
|
def _load_controllers(self):
|
||||||
files_controller = FilesController()
|
files_controller = FilesController()
|
||||||
tabs_controller = TabsController()
|
|
||||||
commands_controller = CommandsController()
|
commands_controller = CommandsController()
|
||||||
completion_controller = CompletionController()
|
completion_controller = CompletionController()
|
||||||
source_views_controller = SourceViewsController()
|
source_views_controller = SourceViewsController()
|
||||||
|
|
||||||
# self.controller_manager.register_controller("base", self)
|
# self.controller_manager.register_controller("base", self)
|
||||||
self.controller_manager.register_controller("files", files_controller)
|
self.controller_manager.register_controller("files", files_controller)
|
||||||
self.controller_manager.register_controller("tabs", tabs_controller)
|
|
||||||
self.controller_manager.register_controller("commands", commands_controller)
|
self.controller_manager.register_controller("commands", commands_controller)
|
||||||
self.controller_manager.register_controller("completion", completion_controller)
|
self.controller_manager.register_controller("completion", completion_controller)
|
||||||
self.controller_manager.register_controller("source_views", source_views_controller)
|
self.controller_manager.register_controller("source_views", source_views_controller)
|
||||||
self.controller_manager.register_controller("plugins", plugins_controller)
|
self.controller_manager.register_controller("plugins", plugins_controller)
|
||||||
self.controller_manager.register_controller("widgets", widget_registery)
|
self.controller_manager.register_controller("widgets", widget_registery)
|
||||||
|
|
||||||
def get_tabs_widget(self):
|
|
||||||
return self.controller_manager["tabs"].get_tabs_widget()
|
|
||||||
|
|
||||||
def create_source_view(self):
|
def create_source_view(self):
|
||||||
source_view = self.controller_manager["source_views"].create_source_view()
|
source_view = self.controller_manager["source_views"].create_source_view()
|
||||||
self.controller_manager["completion"].register_completer(
|
self.controller_manager["completion"].register_completer(
|
||||||
|
|||||||
@@ -39,3 +39,6 @@ class ControllerBase(Singleton, EmitDispatcher):
|
|||||||
def message_to_selected(self, names: list[str], event: BaseEvent):
|
def message_to_selected(self, names: list[str], event: BaseEvent):
|
||||||
for name in names:
|
for name in names:
|
||||||
self.controller_context.message_to_selected(name, event)
|
self.controller_context.message_to_selected(name, event)
|
||||||
|
|
||||||
|
def register_controller(self, name: str, controller):
|
||||||
|
self.controller_context.register_controller(name, controller)
|
||||||
|
|||||||
@@ -25,3 +25,6 @@ class ControllerContext:
|
|||||||
|
|
||||||
def message_to_selected(self, name: list, event: BaseEvent):
|
def message_to_selected(self, name: list, event: BaseEvent):
|
||||||
raise ControllerContextException("Controller Context 'message_to_selected' must be overriden by Controller Manager...")
|
raise ControllerContextException("Controller Context 'message_to_selected' must be overriden by Controller Manager...")
|
||||||
|
|
||||||
|
def register_controller(self, name: str, controller):
|
||||||
|
raise ControllerContextException("Controller Context 'register_controller' must be overriden by Controller Manager...")
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ class ControllerManager(Singleton, dict):
|
|||||||
|
|
||||||
|
|
||||||
def _crete_controller_context(self) -> ControllerContext:
|
def _crete_controller_context(self) -> ControllerContext:
|
||||||
controller_context = ControllerContext()
|
controller_context = ControllerContext()
|
||||||
controller_context.message_to = self.message_to
|
controller_context.message_to = self.message_to
|
||||||
controller_context.message = self.message
|
controller_context.message = self.message
|
||||||
|
controller_context.register_controller = self.register_controller
|
||||||
|
|
||||||
return controller_context
|
return controller_context
|
||||||
|
|
||||||
|
|||||||
@@ -67,31 +67,26 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
|
|||||||
parent_path = os.getcwd()
|
parent_path = os.getcwd()
|
||||||
|
|
||||||
for manifest_meta in manifest_metas:
|
for manifest_meta in manifest_metas:
|
||||||
path, folder, manifest = manifest_meta.path, manifest_meta.folder, manifest_meta.manifest
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = join(path, "plugin.py")
|
path, \
|
||||||
|
folder, \
|
||||||
|
manifest = manifest_meta.path, manifest_meta.folder, manifest_meta.manifest
|
||||||
|
target = join(path, "plugin.py")
|
||||||
|
|
||||||
if not os.path.exists(target):
|
if not os.path.exists(target):
|
||||||
raise PluginsControllerException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
|
raise PluginsControllerException(
|
||||||
|
"Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load..."
|
||||||
|
)
|
||||||
|
|
||||||
module = self._load_plugin_module(path, folder, target)
|
module = self._load_plugin_module(path, folder, target)
|
||||||
|
|
||||||
if is_pre_launch:
|
self._handle_plugin_execute(is_pre_launch, module, manifest_meta)
|
||||||
self._run_with_pool(module, manifest_meta)
|
except PluginsControllerException as e:
|
||||||
else:
|
logger.info(f"Malformed Plugin: Not loading -->: '{manifest_meta.folder}' !")
|
||||||
GLib.idle_add(
|
|
||||||
self._run_with_pool, module, manifest_meta
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
|
|
||||||
logger.debug(f"Trace: {traceback.print_exc()}")
|
logger.debug(f"Trace: {traceback.print_exc()}")
|
||||||
|
|
||||||
os.chdir(parent_path)
|
os.chdir(parent_path)
|
||||||
|
|
||||||
def _run_with_pool(self, module: type, manifest_meta: ManifestMeta):
|
|
||||||
with ThreadPoolExecutor(max_workers = 1) as executor:
|
|
||||||
executor.submit(self.execute_plugin, module, manifest_meta)
|
|
||||||
|
|
||||||
def _load_plugin_module(self, path, folder, target):
|
def _load_plugin_module(self, path, folder, target):
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
|
|
||||||
@@ -105,17 +100,27 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
|
|||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
def create_plugin_context(self):
|
def _handle_plugin_execute(
|
||||||
plugin_context: PluginContext = PluginContext()
|
self, is_pre_launch: bool, module, manifest_meta
|
||||||
|
):
|
||||||
|
if not is_pre_launch:
|
||||||
|
GLib.idle_add(
|
||||||
|
self._run_with_pool, module, manifest_meta
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
plugin_context.requests_ui_element: callable = self.requests_ui_element
|
self._run_with_pool(module, manifest_meta)
|
||||||
plugin_context.message: callable = self.message
|
|
||||||
plugin_context.message_to: callable = self.message_to
|
|
||||||
plugin_context.message_to_selected: callable = self.message_to_selected
|
|
||||||
plugin_context.emit: callable = event_system.emit
|
|
||||||
plugin_context.emit_and_await: callable = event_system.emit_and_await
|
|
||||||
|
|
||||||
return plugin_context
|
def _run_with_pool(self, module: type, manifest_meta: ManifestMeta):
|
||||||
|
with ThreadPoolExecutor(max_workers = 1) as executor:
|
||||||
|
future = executor.submit(self.execute_plugin, module, manifest_meta)
|
||||||
|
future.add_done_callback(self._handle_future_exception)
|
||||||
|
|
||||||
|
def _handle_future_exception(self, future):
|
||||||
|
try:
|
||||||
|
future.result()
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Plugin crashed during execution...")
|
||||||
|
|
||||||
def pre_launch_plugins(self) -> None:
|
def pre_launch_plugins(self) -> None:
|
||||||
logger.info(f"Loading pre-launch plugins...")
|
logger.info(f"Loading pre-launch plugins...")
|
||||||
@@ -142,6 +147,18 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
|
|||||||
|
|
||||||
self._plugin_collection.append(manifest_meta)
|
self._plugin_collection.append(manifest_meta)
|
||||||
|
|
||||||
|
def create_plugin_context(self):
|
||||||
|
plugin_context: PluginContext = PluginContext()
|
||||||
|
|
||||||
|
plugin_context.requests_ui_element: callable = self.requests_ui_element
|
||||||
|
plugin_context.message: callable = self.message
|
||||||
|
plugin_context.message_to: callable = self.message_to
|
||||||
|
plugin_context.message_to_selected: callable = self.message_to_selected
|
||||||
|
plugin_context.emit: callable = event_system.emit
|
||||||
|
plugin_context.emit_and_await: callable = event_system.emit_and_await
|
||||||
|
plugin_context.register_controller: callable = self.register_controller
|
||||||
|
|
||||||
|
return plugin_context
|
||||||
|
|
||||||
|
|
||||||
plugins_controller = PluginsController()
|
plugins_controller = PluginsController()
|
||||||
|
|||||||
@@ -38,3 +38,7 @@ class PluginContext:
|
|||||||
|
|
||||||
def emit_and_await(self, event_type: str, data: tuple = ()):
|
def emit_and_await(self, event_type: str, data: tuple = ()):
|
||||||
raise PluginContextException("Plugin Context 'emit_and_await' must be overridden...")
|
raise PluginContextException("Plugin Context 'emit_and_await' must be overridden...")
|
||||||
|
|
||||||
|
def register_controller(self, name: str, controller):
|
||||||
|
raise PluginContextException("Plugin Context 'register_controller' must be overridden...")
|
||||||
|
|
||||||
|
|||||||
@@ -42,3 +42,6 @@ class PluginCode(PluginBase):
|
|||||||
|
|
||||||
def message_to_selected(self, names: list[str], event: BaseEvent):
|
def message_to_selected(self, names: list[str], event: BaseEvent):
|
||||||
return self.plugin_context.message_to_selected(names, event)
|
return self.plugin_context.message_to_selected(names, event)
|
||||||
|
|
||||||
|
def register_controller(self, name: str, controller):
|
||||||
|
return self.plugin_context.register_controller(name, controller)
|
||||||
|
|||||||
Reference in New Issue
Block a user