Moved plugins and refactor command system

- Moved plugins to apropriate sub folders
- Refactor command system with new add_command method and rename GetCommandSystemEvent to GetNewCommandSystemEvent
- Add RegisterCommandEvent for dynamic command registration
- Change footer container orientation to VERTICAL
- Add search-highlight tag to source buffer
- Add file change detection (deleted, externally modified) in source_file
- Add JSON prettify option to source view popup menu
- Enable hexpand on VTE widget
- Update plugins_controller_mixin to use widget_registry
This commit is contained in:
2026-02-18 23:45:07 -06:00
parent 69c8418a72
commit 6714053776
51 changed files with 176 additions and 68 deletions

View File

@@ -6,8 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from libs.event_factory import Event_Factory, Code_Event_Types
from plugins.plugin_types import PluginCode
@@ -22,7 +21,7 @@ class Plugin(PluginCode):
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
...
def load(self):

View File

@@ -30,13 +30,13 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
return 'Example Code Completion'
def do_match(self, context):
# word = context.get_word()
# if not word or len(word) < 2: return False
# Note: If provider is in interactive activation then need to check
# view focus as otherwise non focus views start trying to grab it.
completion = context.get_property("completion")
if not completion.get_view().has_focus(): return
""" Get whether the provider match the context of completion detailed in context. """
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
return True
def do_get_priority(self):

View File

@@ -1,4 +1,5 @@
# Python imports
from concurrent.futures import ThreadPoolExecutor
import re
# Lib imports
@@ -21,6 +22,9 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
# Note: Using asyncio.run causes a keyboard trap that prevents app
# closure from terminal. ThreadPoolExecutor seems to not have such issues...
self.executor = ThreadPoolExecutor(max_workers = 1)
self.matchers: dict = {
"hello": {
"label": "Hello, World!",

View File

@@ -6,8 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from libs.event_factory import Event_Factory, Code_Event_Types
from plugins.plugin_types import PluginCode
@@ -22,7 +21,7 @@ class Plugin(PluginCode):
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
...
def load(self):

View File

@@ -1,4 +1,5 @@
# Python imports
from concurrent.futures import ThreadPoolExecutor
# Lib imports
import gi
@@ -17,9 +18,13 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
self.executor = ThreadPoolExecutor(max_workers = 1)
self.matchers: dict = {}
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
...
buffer = event.file.buffer
self.executor.submit(self._handle_change, buffer)
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
...
@@ -28,8 +33,13 @@ class ProviderResponseCache(ProviderResponseCacheBase):
...
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
buffer = event.file.buffer
self.executor.submit(self._handle_change, buffer)
def _handle_change(self, buffer):
...
def filter(self, word: str) -> list[dict]:
return []

View File

@@ -6,8 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from libs.event_factory import Event_Factory, Code_Event_Types
from plugins.plugin_types import PluginCode
@@ -22,7 +21,7 @@ class Plugin(PluginCode):
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
...
def load(self):

View File

@@ -6,8 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from libs.event_factory import Event_Factory
from libs.event_factory import Event_Factory, Code_Event_Types
from plugins.plugin_types import PluginCode
@@ -22,7 +21,7 @@ class Plugin(PluginCode):
self.provider: Provider = None
def _controller_message(self, event: BaseEvent):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
...
def load(self):

View File

@@ -30,6 +30,11 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
return 'Words Completion'
def do_match(self, context):
# Note: If provider is in interactive activation then need to check
# view focus as otherwise non focus views start trying to grab it.
completion = context.get_property("completion")
if not completion.get_view().has_focus(): return
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
return True

View File

@@ -1,5 +1,5 @@
# Python imports
import asyncio
from concurrent.futures import ThreadPoolExecutor
# Lib imports
import gi
@@ -24,7 +24,8 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
buffer = event.file.buffer
asyncio.run( self._handle_change(buffer) )
with ThreadPoolExecutor(max_workers = 1) as executor:
executor.submit(self._handle_change, buffer)
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
self.matchers[event.file.buffer] = set()
@@ -35,13 +36,14 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
buffer = event.file.buffer
asyncio.run( self._handle_change(buffer) )
with ThreadPoolExecutor(max_workers = 1) as executor:
executor.submit(self._handle_change, buffer)
async def _handle_change(self, buffer):
def _handle_change(self, buffer):
start_itr = buffer.get_start_iter()
end_itr = buffer.get_end_iter()
data = buffer.get_text(start_itr, end_itr, False)
if not data:
GLib.idle_add(self.load_empty_set, buffer)
return
@@ -67,6 +69,9 @@ class ProviderResponseCache(ProviderResponseCacheBase):
buffer = self.get_iter_correctly(context).get_buffer()
word = self.get_word(context).rstrip()
if not buffer in self.matchers:
self.matchers[buffer] = set()
response: list[dict] = []
for entry in self.matchers[buffer]:
if not entry.rstrip().startswith(word): continue
@@ -81,7 +86,6 @@ class ProviderResponseCache(ProviderResponseCacheBase):
return response
def load_empty_set(self, buffer):
self.matchers[buffer] = set()

View File

@@ -8,11 +8,11 @@ from gi.repository import Gtk
# Application imports
from libs.dto.base_event import BaseEvent
from plugins.plugin_base import PluginBase
from plugins.plugin_types import PluginUI
class Plugin(PluginBase):
class Plugin(PluginUI):
def __init__(self):
super(Plugin, self).__init__()
@@ -21,14 +21,14 @@ class Plugin(PluginBase):
...
def load(self):
ui_element = self.requests_ui_element("plugin_control_list")
ui_element = self.requests_ui_element("header-container")
ui_element.add( self.generate_plugin_element() )
def run(self):
...
def generate_plugin_element(self):
button = Gtk.Button(label = self.name)
button = Gtk.Button(label = "Hello, World!")
button.connect("button-release-event", self.send_message)
button.show()
@@ -36,6 +36,5 @@ class Plugin(PluginBase):
return button
def send_message(self, widget = None, eve = None):
message = "Hello, World!"
self.emit("display_message", ("warning", message, None))
logger.info("Hello, World!")