refactor(command-system): standardize command execution with *args/**kwargs

- Refactor exec_with_args to use *args/**kwargs instead of tuple arguments
- Add *args/**kwargs to all command execute functions for consistency
- Support multiple key bindings per command in registration
- Add character-based key binding support via get_char() in KeyMapper
- Make execute_plugin async and use asyncio.run for plugin execution
- Use MIME type from Gio content_type instead of language for ftype
This commit is contained in:
2026-02-24 22:30:07 -06:00
parent 824dd93696
commit 24bf1e471b
45 changed files with 169 additions and 69 deletions

View File

@@ -9,7 +9,6 @@ from gi.repository import GtkSource
def set_language_and_style(view, file): def set_language_and_style(view, file):
language = view.language_manager.guess_language(file.fname, None) language = view.language_manager.guess_language(file.fname, None)
file.ftype = "buffer" if not language else language
file.buffer.set_language(language) file.buffer.set_language(language)
file.buffer.set_style_scheme(view.syntax_theme) file.buffer.set_style_scheme(view.syntax_theme)

View File

@@ -26,16 +26,13 @@ class CommandSystem:
method = getattr(commands, command) method = getattr(commands, command)
args, kwargs = self.data args, kwargs = self.data
if kwargs: return method.execute(*args, **kwargs)
return method.execute(*args, kwargs)
else:
return method.execute(*args)
def exec_with_args(self, command: str, args: list) -> any: def exec_with_args(self, command: str, *args, **kwargs) -> any:
if not hasattr(commands, command): return if not hasattr(commands, command): return
method = getattr(commands, command) method = getattr(commands, command)
return method.execute(*args) return method.execute(*args, **kwargs)
def add_command(self, command_name: str, command: callable): def add_command(self, command_name: str, command: callable):
setattr(commands, command_name, command) setattr(commands, command_name, command)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Buffer Redo") logger.debug("Command: Buffer Redo")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Buffer Undo") logger.debug("Command: Buffer Undo")

View File

@@ -13,7 +13,9 @@ from ..command_helpers import update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Close File") logger.debug("Command: Close File")
view.command.remove_file(view) view.command.remove_file(view)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Cut to Temp Buffer") logger.debug("Command: Cut to Temp Buffer")

View File

@@ -15,7 +15,9 @@ from ..command_helpers import update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
uri: str uri: str,
*args,
**kwargs
): ):
logger.debug("Command: DnD Load File To Buffer") logger.debug("Command: DnD Load File To Buffer")
file = view.command.new_file(view) file = view.command.new_file(view)
@@ -23,7 +25,7 @@ def execute(
gfile = Gio.File.new_for_uri(uri) gfile = Gio.File.new_for_uri(uri)
view.command.exec_with_args( view.command.exec_with_args(
"load_file", "load_file",
(view, gfile, file) view, gfile, file
) )
view.set_buffer(file.buffer) view.set_buffer(file.buffer)

View File

@@ -14,7 +14,9 @@ from gi.repository import Gio
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
uris: list = [] uris: list = [],
*args,
**kwargs
): ):
logger.debug("Command: DnD Load Files") logger.debug("Command: DnD Load Files")
for uri in uris: for uri in uris:
@@ -23,4 +25,4 @@ def execute(
except Exception as e: except Exception as e:
gfile = Gio.File.new_for_path(uri) gfile = Gio.File.new_for_path(uri)
view.command.exec_with_args("load_file", (view, gfile)) view.command.exec_with_args("load_file", view, gfile)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Duplicate Line") logger.debug("Command: Duplicate Line")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Focus Left Sibling") logger.debug("Command: Focus Left Sibling")
if not view.sibling_left: return if not view.sibling_left: return

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Focus Right Sibling") logger.debug("Command: Focus Right Sibling")
if not view.sibling_right: return if not view.sibling_right: return

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Get Current File") logger.debug("Command: Get Current File")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Get File Type") logger.debug("Command: Get File Type")
file = view.command.get_file(view) file = view.command.get_file(view)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Get Text") logger.debug("Command: Get Text")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Go-To") logger.debug("Command: Go-To")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Has Focus") logger.debug("Command: Has Focus")
ctx = view.get_parent().get_style_context() ctx = view.get_parent().get_style_context()

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Line Down") logger.debug("Command: Line Down")
view.emit("move-lines", True) view.emit("move-lines", True)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Line Up") logger.debug("Command: Line Up")
view.emit("move-lines", False) view.emit("move-lines", False)

View File

@@ -18,6 +18,8 @@ def execute(
view: GtkSource.View, view: GtkSource.View,
gfile: Gio.File, gfile: Gio.File,
file: SourceFile = None, file: SourceFile = None,
*args,
**kwargs
): ):
logger.debug("Command: Load File") logger.debug("Command: Load File")
if not file: if not file:

View File

@@ -14,6 +14,8 @@ from gi.repository import Gio
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Load Start File(s)") logger.debug("Command: Load Start File(s)")
@@ -28,7 +30,7 @@ def execute(
view.command.exec_with_args( view.command.exec_with_args(
"load_file", "load_file",
(view, gfile, file) view, gfile, file
) )
if not starting_files: return if not starting_files: return
@@ -37,4 +39,4 @@ def execute(
file = file.replace("FILE|", "") file = file.replace("FILE|", "")
gfile = Gio.File.new_for_path(file) gfile = Gio.File.new_for_path(file)
view.command.exec_with_args("load_file", (view, gfile)) view.command.exec_with_args("load_file", view, gfile)

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Move To Left Sibling") logger.debug("Command: Move To Left Sibling")
if not view.sibling_left: return if not view.sibling_left: return

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Move To Right Sibling") logger.debug("Command: Move To Right Sibling")
if not view.sibling_right: return if not view.sibling_right: return

View File

@@ -14,7 +14,9 @@ from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: New File") logger.debug("Command: New File")

View File

@@ -13,7 +13,9 @@ from ..command_helpers import update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Open File(s)") logger.debug("Command: Open File(s)")
gfiles = event_system.emit_and_await("open-files") gfiles = event_system.emit_and_await("open-files")
@@ -22,9 +24,9 @@ def execute(
file = view.command.get_file(view) file = view.command.get_file(view)
if file.ftype == "buffer": if file.ftype == "buffer":
gfile = gfiles.pop() gfile = gfiles.pop()
view.command.exec_with_args("load_file", (view, gfile, file)) view.command.exec_with_args("load_file", view, gfile, file)
view.set_buffer(file.buffer) view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view) update_info_bar_if_focused(view.command, view)
for i, gfile in enumerate(gfiles): for i, gfile in enumerate(gfiles):
view.command.exec_with_args("load_file", (view, gfile)) view.command.exec_with_args("load_file", view, gfile)

View File

@@ -13,7 +13,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Paste Temp Buffer") logger.debug("Command: Paste Temp Buffer")

View File

@@ -13,7 +13,9 @@ from ..command_helpers import set_language_and_style
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Save File") logger.debug("Command: Save File")
file = view.command.get_file(view) file = view.command.get_file(view)

View File

@@ -13,7 +13,9 @@ from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.info("Command: Save File As") logger.info("Command: Save File As")
file = view.command.get_file(view) file = view.command.get_file(view)

View File

@@ -16,7 +16,9 @@ from ..command_helpers import update_info_bar_if_focused
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
file: SourceFile file: SourceFile,
*args,
**kwargs
): ):
logger.debug("Command: Set Buffer") logger.debug("Command: Set Buffer")

View File

@@ -13,7 +13,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
language: str language: str,
*args,
**kwargs
): ):
logger.debug("Command: Set Buffer Language") logger.debug("Command: Set Buffer Language")

View File

@@ -13,7 +13,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
style: str style: str,
*args,
**kwargs
): ):
logger.debug("Command: Set Buffer Style") logger.debug("Command: Set Buffer Style")

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Set Focus Border") logger.debug("Command: Set Focus Border")
ctx = view.get_parent().get_style_context() ctx = view.get_parent().get_style_context()

View File

@@ -13,6 +13,8 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Set MiniView") logger.debug("Command: Set MiniView")
event_system.emit("set-mini-view", (view,)) event_system.emit("set-mini-view", (view,))

View File

@@ -12,7 +12,9 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Show Completion") logger.debug("Command: Show Completion")
completer = view.get_completion() completer = view.get_completion()

View File

@@ -13,6 +13,8 @@ from gi.repository import GtkSource
def execute( def execute(
view: GtkSource.View, view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Update Info Bar") logger.debug("Command: Update Info Bar")
file = view.command.get_file(view) file = view.command.get_file(view)

View File

@@ -13,7 +13,9 @@ from gi.repository import Pango
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Zoom In") logger.debug("Command: Zoom In")

View File

@@ -13,7 +13,9 @@ from gi.repository import Pango
def execute( def execute(
view: GtkSource.View = None view: GtkSource.View,
*args,
**kwargs
): ):
logger.debug("Command: Zoom Out") logger.debug("Command: Zoom Out")

View File

@@ -40,12 +40,16 @@ class SourceViewsController(ControllerBase, list):
self.signal_mapper.insert_text(event.file, event.text) self.signal_mapper.insert_text(event.file, event.text)
def _register_command(self, event: Code_Event_Types.RegisterCommandEvent): def _register_command(self, event: Code_Event_Types.RegisterCommandEvent):
self.state_manager.key_mapper.map_command( if not isinstance(event.binding, list):
event.command_name, event.binding = [ event.binding ]
{
f"{event.binding_mode}": event.binding for binding in event.binding:
} self.state_manager.key_mapper.map_command(
) event.command_name,
{
f"{event.binding_mode}": binding
}
)
for view in self: for view in self:
view.command.add_command( view.command.add_command(

View File

@@ -22,7 +22,8 @@ class SourceViewsInsertState:
event = Event_Factory.create_event("focused_view", view = source_view) event = Event_Factory.create_event("focused_view", view = source_view)
emit(event) emit(event)
def insert_text(self, file, text): def insert_text(self, file, text: str):
return True return True
def move_cursor(self, source_view, step, count, extend_selection, emit): def move_cursor(self, source_view, step, count, extend_selection, emit):
@@ -51,21 +52,23 @@ class SourceViewsInsertState:
def key_press_event(self, source_view, eve, key_mapper): def key_press_event(self, source_view, eve, key_mapper):
command = key_mapper._key_press_event(eve) command = key_mapper._key_press_event(eve)
is_future = key_mapper._key_release_event(eve) is_future = key_mapper._key_release_event(eve)
char_str = key_mapper.get_char(eve)
if is_future: return True if is_future: return True
if not command: return False if not command: return False
source_view.command.exec(command) source_view.command.exec_with_args(command, source_view, char_str)
return True return True
def key_release_event(self, source_view, eve, key_mapper): def key_release_event(self, source_view, eve, key_mapper):
command = key_mapper._key_release_event(eve) command = key_mapper._key_release_event(eve)
is_past = key_mapper._key_press_event(eve) is_past = key_mapper._key_press_event(eve)
char_str = key_mapper.get_char(eve)
if is_past: return True if is_past: return True
if not command: return False if not command: return False
source_view.command.exec(command) source_view.command.exec_with_args(command, source_view, char_str)
return True return True

View File

@@ -96,19 +96,28 @@ class KeyMapper:
getattr(self.states[state], press_state)[keyname] = command getattr(self.states[state], press_state)[keyname] = command
def _key_press_event(self, eve): def _key_press_event(self, eve):
keyname = Gdk.keyval_name(eve.keyval).lower() keyname = self.get_keyname(eve)
char_str = self.get_char(eve)
self._set_key_state(eve) self._set_key_state(eve)
if keyname in self.states[self.state].held: if keyname in self.states[self.state].held:
return self.states[self.state].held[keyname] return self.states[self.state].held[keyname]
if char_str in self.states[self.state].held:
return self.states[self.state].held[char_str]
def _key_release_event(self, eve): def _key_release_event(self, eve):
keyname = Gdk.keyval_name(eve.keyval).lower() keyname = self.get_keyname(eve)
char_str = self.get_char(eve)
self._set_key_state(eve) self._set_key_state(eve)
if keyname in self.states[self.state].released: if keyname in self.states[self.state].released:
return self.states[self.state].released[keyname] return self.states[self.state].released[keyname]
if char_str in self.states[self.state].released:
return self.states[self.state].released[char_str]
def _set_key_state(self, eve): def _set_key_state(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK) modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
is_control = modifiers & Gdk.ModifierType.CONTROL_MASK is_control = modifiers & Gdk.ModifierType.CONTROL_MASK
@@ -137,3 +146,9 @@ class KeyMapper:
def get_raw_keyname(self, eve): def get_raw_keyname(self, eve):
return Gdk.keyval_name(eve.keyval) return Gdk.keyval_name(eve.keyval)
def get_keyname(self, eve):
return Gdk.keyval_name(eve.keyval).lower()
def get_char(self, eve):
return chr( Gdk.keyval_to_unicode(eve.keyval) )

View File

@@ -33,7 +33,7 @@ class SourceViewDnDMixin:
def _on_uri_data_received(self, uris: []): def _on_uri_data_received(self, uris: []):
uri = uris.pop(0) uri = uris.pop(0)
self.command.exec_with_args("dnd_load_file_to_buffer", (self, uri)) self.command.exec_with_args("dnd_load_file_to_buffer", self, uri)
if not uris: return if not uris: return

View File

@@ -99,8 +99,8 @@ class SourceFile(GtkSource.File):
) )
# Note: 'idle_add' needed b/c markers don't get thir positions # Note: 'idle_add' needed b/c markers don't get thir positions
# updated relative to the initial insert. # updated relative to the initial insert.
# If not used, seg faults galor during multi insert. # If not used, seg faults galor during multi insert.
GLib.idle_add(self.emit, event) GLib.idle_add(self.emit, event)
def _mark_set( def _mark_set(
@@ -136,7 +136,6 @@ class SourceFile(GtkSource.File):
if self.was_deleted: if self.was_deleted:
self.was_deleted = False self.was_deleted = False
# self.set_path(gfile)
self.set_location( None ) self.set_location( None )
self.set_location( gfile ) self.set_location( gfile )
@@ -148,6 +147,11 @@ class SourceFile(GtkSource.File):
self.set_path(gfile) self.set_path(gfile)
text = gfile.load_bytes()[0].get_data().decode("UTF-8") 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)
logger.debug(f"File content type: {self.ftype}")
undo_manager = self.buffer.get_undo_manager() undo_manager = self.buffer.get_undo_manager()
def move_insert_to_start(): def move_insert_to_start():

View File

@@ -31,6 +31,8 @@ class TabWidget(Gtk.Box):
self.set_orientation(0) self.set_orientation(0)
self.set_hexpand(False) self.set_hexpand(False)
self.set_vexpand(False)
self.set_size_request(-1, 12)
def _setup_signals(self): def _setup_signals(self):
... ...

View File

@@ -33,7 +33,9 @@ class ControllerManager(Singleton, dict):
raise ControllerManagerException("Must pass in a 'name' and 'controller'...") raise ControllerManagerException("Must pass in a 'name' and 'controller'...")
if name in self.keys(): if name in self.keys():
raise ControllerManagerException(f"Can't bind controller to registered name of '{name}'...") raise ControllerManagerException(
f"Can't bind controller to existing registered name of '{name}'..."
)
controller.set_controller_context( self._crete_controller_context() ) controller.set_controller_context( self._crete_controller_context() )

View File

@@ -14,7 +14,7 @@ from ..base_event import BaseEvent
@dataclass @dataclass
class RegisterCommandEvent(BaseEvent): class RegisterCommandEvent(BaseEvent):
command_name: str = "" command_name: str = ""
command: callable = None command: callable = None
binding_mode: str = "" binding_mode: str = ""
binding: str = "" binding: str or list = ""

View File

@@ -3,6 +3,7 @@ import os
import sys import sys
import importlib import importlib
import traceback import traceback
import asyncio
from os.path import join from os.path import join
from os.path import isdir from os.path import isdir
@@ -75,9 +76,14 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
module = self._load_plugin_module(path, folder, target) module = self._load_plugin_module(path, folder, target)
if is_pre_launch: if is_pre_launch:
self.execute_plugin(module, manifest_meta) asyncio.run(
self.execute_plugin(module, manifest_meta)
)
else: else:
GLib.idle_add(self.execute_plugin, module, manifest_meta) GLib.idle_add(
asyncio.run,
self.execute_plugin(module, manifest_meta)
)
except Exception as e: except Exception as e:
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !") logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
logger.debug(f"Trace: {traceback.print_exc()}") logger.debug(f"Trace: {traceback.print_exc()}")
@@ -119,7 +125,7 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
manifest_metas: list = self._manifest_manager.get_post_launch_plugins() manifest_metas: list = self._manifest_manager.get_post_launch_plugins()
self._load_plugins(manifest_metas) self._load_plugins(manifest_metas)
def execute_plugin(self, module: type, manifest_meta: ManifestMeta): async def execute_plugin(self, module: type, manifest_meta: ManifestMeta):
plugin = module.Plugin() plugin = module.Plugin()
plugin.plugin_context: PluginContext = self.create_plugin_context() plugin.plugin_context: PluginContext = self.create_plugin_context()