Compare commits

...

11 Commits

123 changed files with 1694 additions and 1229 deletions

View File

@@ -1,13 +1,9 @@
{ {
"manifest": { "name": "Example Plugin",
"name": "Example Plugin", "author": "John Doe",
"author": "John Doe", "version": "0.0.1",
"version": "0.0.1", "support": "",
"support": "", "requests": {
"requests": { "bind_keys": ["Example Plugin||send_message:<Control>f"]
"ui_target": "plugin_control_list",
"pass_events": true,
"bind_keys": ["Example Plugin||send_message:<Control>f"]
}
} }
} }

View File

@@ -1,8 +1,4 @@
# Python imports # Python imports
import os
import threading
import subprocess
import time
# Lib imports # Lib imports
import gi import gi
@@ -10,42 +6,36 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from libs.dto.base_event import BaseEvent
from plugins.plugin_base import PluginBase from plugins.plugin_base import PluginBase
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
return wrapper
# NOTE: Threads WILL die with parent's destruction.
def daemon_threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
return wrapper
class Plugin(PluginBase): class Plugin(PluginBase):
def __init__(self): def __init__(self):
super().__init__() super(Plugin, self).__init__()
self.name = "Example Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
def generate_reference_ui_element(self): def _controller_message(self, event: BaseEvent):
button = Gtk.Button(label=self.name) ...
button.connect("button-release-event", self.send_message)
return button def load(self):
ui_element = self.requests_ui_element("plugin_control_list")
ui_element.add( self.generate_plugin_element() )
def run(self): def run(self):
... ...
def generate_plugin_element(self):
button = Gtk.Button(label = self.name)
def send_message(self, widget=None, eve=None): button.connect("button-release-event", self.send_message)
button.show()
return button
def send_message(self, widget = None, eve = None):
message = "Hello, World!" message = "Hello, World!"
event_system.emit("display_message", ("warning", message, None)) self.emit("display_message", ("warning", message, None))

View File

@@ -9,10 +9,10 @@ import sys
# Application imports # Application imports
# from libs.db import DB # from libs.db import DB
from libs.event_system import EventSystem from libs.event_system import EventSystem
from libs.endpoint_registry import EndpointRegistry
from libs.keybindings import Keybindings from libs.keybindings import Keybindings
from libs.logger import Logger from libs.logger import Logger
from libs.settings.manager import SettingsManager from libs.settings.manager import SettingsManager
from libs.widget_registery import WidgetRegisteryController
@@ -34,12 +34,8 @@ def daemon_threaded_wrapper(fn):
def call_chain_wrapper(fn): def call_chain_wrapper(fn):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
print()
print()
for line in traceback.format_stack(): for line in traceback.format_stack():
print( line.strip() ) print( line.strip() )
print()
print()
return fn(*args, **kwargs) return fn(*args, **kwargs)
return wrapper return wrapper
@@ -51,8 +47,8 @@ builtins.APP_NAME = "<change_me>"
builtins.keybindings = Keybindings() builtins.keybindings = Keybindings()
builtins.event_system = EventSystem() builtins.event_system = EventSystem()
builtins.endpoint_registry = EndpointRegistry()
builtins.settings_manager = SettingsManager() builtins.settings_manager = SettingsManager()
builtins.widget_registery = WidgetRegisteryController()
# builtins.db = DB() # builtins.db = DB()
settings_manager.load_settings() settings_manager.load_settings()

View File

@@ -1,33 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class BuilderWrapper(Gtk.Builder):
"""docstring for BuilderWrapper."""
def __init__(self):
super(BuilderWrapper, self).__init__()
self.objects = {}
def get_object(self, id: str, use_gtk: bool = True) -> any:
if not use_gtk:
return self.objects[id]
return super(BuilderWrapper, self).get_object(id)
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
if not use_gtk:
self.objects[id] = object
else:
super(BuilderWrapper, self).expose_object(id, object)
def dereference_object(self, id: str) -> None:
del self.objects[id]

View File

@@ -14,7 +14,6 @@ class CenterContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(CenterContainer, self).__init__() super(CenterContainer, self).__init__()
self._builder = settings_manager.get_builder()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
@@ -39,7 +38,7 @@ class CenterContainer(Gtk.Box):
... ...
def _load_widgets(self): def _load_widgets(self):
glade_box = self._builder.get_object("glade_box") glade_box = widget_registery.get_object("glade_box")
button = Gtk.Button(label = "Click Me!") button = Gtk.Button(label = "Click Me!")
button.connect("clicked", self._hello_world) button.connect("clicked", self._hello_world)

View File

@@ -6,33 +6,48 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from plugins import plugins_controller
from libs.mixins.ipc_signals_mixin import IPCSignalsMixin from libs.mixins.ipc_signals_mixin import IPCSignalsMixin
from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin
from ..containers.base_container import BaseContainer from ..containers.base_container import BaseContainer
from .base_controller_data import BaseControllerData from .base_controller_mixin import BaseControllerMixin
from .bridge_controller import BridgeController from .bridge_controller import BridgeController
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData): class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerMixin):
""" docstring for BaseController. """ """ docstring for BaseController. """
def __init__(self): def __init__(self):
self._setup_controller_data() self._setup_controller_data()
self._load_plugins(is_pre = True)
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_controllers() self._load_controllers()
self._load_plugins_and_files() self._load_plugins(is_pre = False)
self._load_files()
logger.info(f"Made it past {self.__class__} loading...") logger.info(f"Made it past {self.__class__} loading...")
settings_manager.set_end_load_time() settings_manager.set_end_load_time()
settings_manager.log_load_time() settings_manager.log_load_time()
def _setup_controller_data(self):
self.window = settings_manager.get_main_window()
self.base_container = BaseContainer()
self.plugins_controller = plugins_controller
widget_registery.expose_object("main_window", self.window)
settings_manager.register_signals_to_builder([self, self.base_container])
self._collect_files_dirs()
def _setup_styling(self): def _setup_styling(self):
... ...
@@ -50,23 +65,22 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
def _load_controllers(self): def _load_controllers(self):
BridgeController() BridgeController()
def _load_plugins_and_files(self): def _load_plugins(self, is_pre: bool):
args, unknownargs = settings_manager.get_starting_args() args, unknownargs = settings_manager.get_starting_args()
if args.no_plugins == "false": if args.no_plugins == "true": return
self.plugins_controller.pre_launch_plugins()
self.plugins_controller.post_launch_plugins()
if is_pre:
self.plugins_controller.pre_launch_plugins()
return
if not is_pre:
self.plugins_controller.post_launch_plugins()
return
def _load_files(self):
for file in settings_manager.get_starting_files(): for file in settings_manager.get_starting_files():
event_system.emit("post-file-to-ipc", file) event_system.emit("post-file-to-ipc", file)
def _tggl_top_main_menubar(self): def _tggl_top_main_menubar(self):
logger.debug("_tggl_top_main_menubar > stub...") logger.debug("_tggl_top_main_menubar > stub...")
def _load_glade_file(self):
self.builder.add_from_file( settings_manager.path_manager.get_glade_file() )
self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder)
self.base_container = BaseContainer()
settings_manager.register_signals_to_builder([self, self.base_container])

View File

@@ -6,28 +6,11 @@ from shutil import which
# Lib imports # Lib imports
# Application imports # Application imports
from plugins.plugins_controller import PluginsController
from ..builder_wrapper import BuilderWrapper
class BaseControllerData: class BaseControllerMixin:
''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. ''' ''' BaseControllerMixin contains most of the state of the app at ay given time. It also has some support methods. '''
def _setup_controller_data(self) -> None:
self.window = settings_manager.get_main_window()
self.builder = BuilderWrapper()
self.plugins_controller = PluginsController()
self.base_container = None
self.was_midified_key = False
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
self._collect_files_dirs()
self._load_glade_file()
def _collect_files_dirs(self): def _collect_files_dirs(self):
args, \ args, \

View File

@@ -3,12 +3,15 @@
# Lib imports # Lib imports
# Application imports # Application imports
from .controllers.controller_manager import ControllerManager from plugins import plugins_controller
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.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.source_views_controller import SourceViewsController from .controllers.views.source_views_controller import SourceViewsController
from .mini_view_widget import MiniViewWidget from .mini_view_widget import MiniViewWidget
@@ -31,11 +34,14 @@ class CodeBase:
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("files", files_controller) self.controller_manager.register_controller("files", files_controller)
self.controller_manager.register_controller("tabs", tabs_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("widgets", widget_registery)
def get_tabs_widget(self): def get_tabs_widget(self):
return self.controller_manager["tabs"].get_tabs_widget() return self.controller_manager["tabs"].get_tabs_widget()
@@ -44,7 +50,12 @@ class CodeBase:
return self.miniview_widget return self.miniview_widget
def create_source_view(self): def create_source_view(self):
return self.controller_manager["source_views"].create_source_view() source_view = self.controller_manager["source_views"].create_source_view()
self.controller_manager["completion"].register_completer(
source_view.get_completion()
)
return source_view
def first_map_load(self): def first_map_load(self):
self.controller_manager["source_views"].first_map_load() self.controller_manager["source_views"].first_map_load()

View File

@@ -0,0 +1,5 @@
"""
Code Command System Package
"""
from .command_system import CommandSystem

View File

@@ -9,7 +9,7 @@ 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 = language 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

@@ -3,13 +3,12 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code import CodeEvent from libs.event_factory import Event_Factory, Code_Event_Types
from .event_factory import Event_Factory, Event_Factory_Types
from ..source_view import SourceView
from . import commands from . import commands
from .source_view import SourceView
class CommandSystem: class CommandSystem:
@@ -39,17 +38,18 @@ class CommandSystem:
return method.execute(*args) return method.execute(*args)
def emit(self, event: CodeEvent): def emit(self, event: Code_Event_Types.CodeEvent):
""" Monky patch 'emit' from command controller... """ """ Monkey patch 'emit' from command controller... """
... ...
def emit_to(self, controller: str, event: CodeEvent): def emit_to(self, controller: str, event: Code_Event_Types.CodeEvent):
""" Monky patch 'emit' from command controller... """ """ Monkey patch 'emit_to' from command controller... """
... ...
def get_file(self, view: SourceView): def get_file(self, view: SourceView):
event = Event_Factory.create_get_file( event = Event_Factory.create_event(
"get_file",
view = view, view = view,
buffer = view.get_buffer() buffer = view.get_buffer()
) )
@@ -59,7 +59,8 @@ class CommandSystem:
return event.response return event.response
def get_swap_file(self, view: SourceView): def get_swap_file(self, view: SourceView):
event = Event_Factory.create_get_swap_file( event = Event_Factory.create_event(
"get_swap_file",
view = view, view = view,
buffer = view.get_buffer() buffer = view.get_buffer()
) )
@@ -76,7 +77,8 @@ class CommandSystem:
return event.response return event.response
def remove_file(self, view: SourceView): def remove_file(self, view: SourceView):
event = Event_Factory.create_remove_file( event = Event_Factory.create_event(
"removed_file",
view = view, view = view,
buffer = view.get_buffer() buffer = view.get_buffer()
) )

View File

@@ -1,5 +1,5 @@
""" """
Commands Package Code Commands Package
""" """
import pkgutil import pkgutil

View File

@@ -9,7 +9,6 @@ from gi.repository import GtkSource
from gi.repository import Gio from gi.repository import Gio
# Application imports # Application imports
from ..source_file import SourceFile

View File

@@ -9,7 +9,7 @@ from gi.repository import GtkSource
from gi.repository import Gio from gi.repository import Gio
# Application imports # Application imports
from ..source_file import SourceFile from ...source_file import SourceFile
from ..command_helpers import set_language_and_style from ..command_helpers import set_language_and_style

View File

@@ -18,7 +18,7 @@ def execute(
): ):
logger.debug("Command: New File") logger.debug("Command: New File")
file = view.command.new_file(view) file = view.command.new_file(view)
set_language_and_style(view, file) set_language_and_style(view, file)
view.set_buffer(file.buffer) view.set_buffer(file.buffer)

View File

@@ -9,7 +9,7 @@ gi.require_version('GtkSource', '4')
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports # Application imports
from ..source_file import SourceFile from ...source_file import SourceFile
from ..command_helpers import update_info_bar_if_focused from ..command_helpers import update_info_bar_if_focused

View File

@@ -6,12 +6,8 @@ import gi
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GtkSource from gi.repository import GtkSource
from gi.repository import Gio
# Application imports # Application imports
from libs.dto.code import FocusedViewEvent
from ..source_file import SourceFile

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Show Completion")
completer = view.get_completion()
providers = completer.get_providers()
if not providers:
view.command.request_completion(view)
return
completer.start(
providers,
completer.create_context()
)

View File

@@ -6,10 +6,8 @@ import gi
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GtkSource from gi.repository import GtkSource
from gi.repository import Gio
# Application imports # Application imports
from ..source_file import SourceFile

View File

@@ -1,18 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Show Completion")
view.command.request_completion(view)

View File

@@ -1,3 +1,3 @@
""" """
Custom Completion Providers Module Code Completion Providers Package
""" """

View File

@@ -1,84 +0,0 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk
from gi.repository import GtkSource
from gi.repository import GObject
# Application imports
class ExampleCompletionProvider(GObject.GObject, GtkSource.CompletionProvider):
"""
This is a custom Completion Example Provider.
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
"""
__gtype_name__ = 'ExampleCompletionProvider'
def __init__(self):
GObject.Object.__init__(self)
def do_get_name(self):
""" Returns: a new string containing the name of the provider. """
return 'Example Completion Provider'
def do_match(self, context):
""" Get whether the provider match the context of completion detailed in context. """
# NOTE: True for debugging but context needs to normally get checked for actual usage needs.
# TODO: Fix me
return True
def do_get_priority(self):
""" Determin position in result list along other providor results. """
return 1
# def do_get_activation(self):
# """ The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE
# return GtkSource.CompletionActivation.USER_REQUESTED
# return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context):
"""
In this instance, it will do 2 things:
1) always provide Hello World! (Not ideal but an option so its in the example)
2) Utilizes the Gtk.TextIter from the TextBuffer to determine if there is a jinja
example of '{{ custom.' if so it will provide you with the options of foo and bar.
If selected it will insert foo }} or bar }}, completing your syntax...
PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
"""
proposals = [
GtkSource.CompletionItem(label='Hello World!', text = 'Hello World!', icon = None, info = None) # NOTE: Always proposed...
]
# Gtk Versions differ on get_iter responses...
end_iter = context.get_iter()
if not isinstance(end_iter, Gtk.TextIter):
_, end_iter = context.get_iter()
if end_iter:
buf = end_iter.get_buffer()
mov_iter = end_iter.copy()
if mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY):
mov_iter, _ = mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY)
left_text = buf.get_text(mov_iter, end_iter, True)
else:
left_text = ''
if re.match(r'.*\{\{\s*custom\.$', left_text):
proposals.append(
GtkSource.CompletionItem(label='foo', text='foo }}') # optionally proposed based on left search via regex
)
proposals.append(
GtkSource.CompletionItem(label='bar', text='bar }}') # optionally proposed based on left search via regex
)
context.add_proposals(self, proposals, True)

View File

@@ -1,137 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk
from gi.repository import GtkSource
from gi.repository import GObject
# Application imports
class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
"""
This code is an LSP code completion plugin for Newton.
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
"""
__gtype_name__ = 'LSPProvider'
def __init__(self):
GObject.Object.__init__(self)
self._icon_theme = Gtk.IconTheme.get_default()
self.lsp_data = None
def pre_populate(self, context):
...
def do_get_name(self):
return "LSP Code Completion"
def get_iter_correctly(self, context):
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
def do_match(self, context):
iter = self.get_iter_correctly(context)
iter.backward_char()
buffer = iter.get_buffer()
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False
ch = iter.get_char()
# NOTE: Look to re-add or apply supprting logic to use spaces
# As is it slows down the editor in certain contexts...
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
if not (ch in ('_', '.') or ch.isalnum()):
return False
return True
def do_get_priority(self):
return 5
def do_populate(self, context, items = []):
# self.lsp_data
proposals = []
comp_item = GtkSource.CompletionItem.new()
comp_item.set_label("LSP Class")
comp_item.set_text("LSP Code")
# comp_item.set_icon(self.get_icon_for_type(completion.type))
comp_item.set_info("A test LSP completion item...")
context.add_proposals(self, [comp_item], True)
# def do_populate(self, context, items = []):
# if hasattr(self._source_view, "completion_items"):
# items = self._source_view.completion_items
# proposals = []
# for item in items:
# proposals.append( self.create_completion_item(item) )
# context.add_proposals(self, proposals, True)
# def get_icon_for_type(self, _type):
# try:
# return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
# except:
# ...
# try:
# return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
# except:
# ...
# return None
# def create_completion_item(self, item):
# comp_item = GtkSource.CompletionItem.new()
# keys = item.keys()
# comp_item.set_label(item["label"])
# if "insertText" in keys:
# comp_item.set_text(item["insertText"])
# if "additionalTextEdits" in keys:
# comp_item.additionalTextEdits = item["additionalTextEdits"]
# return comp_item
# def create_completion_item(self, item):
# comp_item = GtkSource.CompletionItem.new()
# comp_item.set_label(item.label)
# if item.textEdit:
# if isinstance(item.textEdit, dict):
# comp_item.set_text(item.textEdit["newText"])
# else:
# comp_item.set_text(item.textEdit)
# else:
# comp_item.set_text(item.insertText)
# comp_item.set_icon( self.get_icon_for_type(item.kind) )
# comp_item.set_info(item.documentation)
# return comp_item

View File

@@ -0,0 +1,87 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
# Application imports
class ProviderResponseCacheException(Exception):
...
class ProviderResponseCacheBase:
def __init__(self):
super(ProviderResponseCacheBase, self).__init__()
self._icon_theme = Gtk.IconTheme.get_default()
def process_file_load(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_load' not implemented...")
def process_file_close(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_close' not implemented...")
def process_file_save(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_save' not implemented...")
def process_file_change(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...")
def filter(self, word: str):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter' not implemented...")
def filter_with_context(self, context: GtkSource.CompletionContext):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
def create_completion_item(
self,
label: str = "",
text: str = "",
info: str = "",
completion: any = None
):
if not label or not text: return
comp_item = GtkSource.CompletionItem.new()
comp_item.set_label(label)
comp_item.set_text(text)
if info:
comp_item.set_info(info)
# comp_item.set_markup(f"<h3>{info}</h3>")
if completion:
comp_item.set_icon(
self.get_icon_for_type(completion.type)
)
return comp_item
def get_word(self, context):
start_iter = context.get_iter()
end_iter = None
if not isinstance(start_iter, Gtk.TextIter):
_, start_iter = context.get_iter()
end_iter = start_iter.copy()
if not start_iter.starts_word():
start_iter.backward_word_start()
end_iter.forward_word_end()
buffer = start_iter.get_buffer()
return buffer.get_text(start_iter, end_iter, False)

View File

@@ -1,107 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk
from gi.repository import GtkSource
from gi.repository import GObject
import jedi
from jedi.api import Script
# Application imports
# FIXME: Find real icon names...
icon_names = {
'import': '',
'module': '',
'class': '',
'function': '',
'statement': '',
'param': ''
}
class Jedi:
def get_script(file, doc_text):
return Script(code = doc_text, path = file)
class PythonCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
"""
This code is A python code completion plugin for Newton.
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
"""
__gtype_name__ = 'PythonProvider'
def __init__(self, file):
GObject.Object.__init__(self)
self._theme = Gtk.IconTheme.get_default()
self._file = file
def do_get_name(self):
return "Python Code Completion"
def get_iter_correctly(self, context):
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
def do_match(self, context):
iter = self.get_iter_correctly(context)
iter.backward_char()
buffer = iter.get_buffer()
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False
ch = iter.get_char()
# NOTE: Look to re-add or apply supprting logic to use spaces
# As is it slows down the editor in certain contexts...
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
if not (ch in ('_', '.') or ch.isalnum()):
return False
return True
def do_get_priority(self):
return 1
def do_get_activation(self):
return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context):
# TODO: Maybe convert async?
it = self.get_iter_correctly(context)
buffer = it.get_buffer()
proposals = []
doc_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
iter_cursor = buffer.get_iter_at_mark(buffer.get_insert())
linenum = iter_cursor.get_line() + 1
charnum = iter_cursor.get_line_index()
def create_generator():
for completion in Jedi.get_script(self._file, doc_text).complete(line = linenum, column = None, fuzzy = False):
comp_item = GtkSource.CompletionItem.new()
comp_item.set_label(completion.name)
comp_item.set_text(completion.name)
comp_item.set_icon(self.get_icon_for_type(completion.type))
comp_item.set_info(completion.docstring())
yield comp_item
for item in create_generator():
proposals.append(item)
context.add_proposals(self, proposals, True)
def get_icon_for_type(self, _type):
try:
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
except (KeyError, AttributeError, GObject.GError) as e:
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
except (GObject.GError, AttributeError) as e:
return None

View File

@@ -1,3 +1,3 @@
""" """
Controllers Package Code Controllers Package
""" """

View File

@@ -3,16 +3,12 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code import ( from libs.controllers.controller_base import ControllerBase
CodeEvent,
GetCommandSystemEvent, from libs.event_factory import Code_Event_Types
FocusedViewEvent
)
from ..command_system import CommandSystem from ..command_system import CommandSystem
from .controller_base import ControllerBase
class CommandsController(ControllerBase, list): class CommandsController(ControllerBase, list):
@@ -20,8 +16,8 @@ class CommandsController(ControllerBase, list):
super(CommandsController, self).__init__() super(CommandsController, self).__init__()
def _controller_message(self, event: CodeEvent): def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, GetCommandSystemEvent): if isinstance(event, Code_Event_Types.GetCommandSystemEvent):
event.response = self.get_command_system() event.response = self.get_command_system()
def get_command_system(self): def get_command_system(self):

View File

@@ -8,19 +8,8 @@ from gi.repository import GLib
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports # Application imports
from libs.dto.code import ( from libs.controllers.controller_base import ControllerBase
CodeEvent, from libs.event_factory import Event_Factory, Code_Event_Types
FocusedViewEvent,
RequestCompletionEvent,
CursorMovedEvent,
TextChangedEvent,
TextInsertedEvent
)
from ..completion_providers.example_completion_provider import ExampleCompletionProvider
from ..completion_providers.lsp_completion_provider import LSPCompletionProvider
from .controller_base import ControllerBase
@@ -28,84 +17,78 @@ class CompletionController(ControllerBase):
def __init__(self): def __init__(self):
super(CompletionController, self).__init__() super(CompletionController, self).__init__()
self._completor: GtkSource.Completion = None self.words_provider = GtkSource.CompletionWords.new("words", None)
self._timeout_id: int = None self.words_provider.props.activation = GtkSource.CompletionActivation.INTERACTIVE
self._lsp_provider: LSPCompletionProvider = LSPCompletionProvider()
self._completers: list[GtkSource.Completion] = []
self._providers: dict[str, GtkSource.CompletionProvider] = {}
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.RegisterProviderEvent):
self.register_provider(
event.provider_name,
event.provider,
event.language_ids
)
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.provider_process_file_load(event)
elif isinstance(event, Code_Event_Types.RemovedFileEvent):
self.provider_process_file_close(event)
elif isinstance(event, Code_Event_Types.SavedFileEvent):
self.provider_process_file_save(event)
elif isinstance(event, Code_Event_Types.TextChangedEvent):
self.provider_process_file_change(event)
# elif isinstance(event, Code_Event_Types.RequestCompletionEvent):
# self.request_unbound_completion( event.view.get_completion() )
def _controller_message(self, event: CodeEvent): def register_completer(self, completer: GtkSource.Completion):
if isinstance(event, FocusedViewEvent): self._completers.append(completer)
self._completor = event.view.get_completion()
if not self._timeout_id: return completer.add_provider(self.words_provider)
for provider in self._providers.values():
GLib.source_remove(self._timeout_id) completer.add_provider(provider)
self._timeout_id = None
elif isinstance(event, RequestCompletionEvent):
self.request_completion()
# elif isinstance(event, TextInsertedEvent):
# self.request_completion()
def _process_request_completion(self):
self._start_completion()
self._timeout_id = None
return False
def _do_completion(self):
if self._completor.get_providers():
self._match_completion()
else:
self._start_completion()
def _match_completion(self):
"""
Note: Use IF providers were added to completion...
"""
self._completion.match(
self._completion.create_context()
)
def _start_completion(self):
"""
Note: Use IF NO providers have been added to completion...
"""
self._completor.start(
[
ExampleCompletionProvider(),
self._lsp_provider
],
self._completor.create_context()
)
def set_completer(self, completer):
self._completor = completer
def request_completion(self):
if self._timeout_id:
GLib.source_remove(self._timeout_id)
self._timeout_id = GLib.timeout_add(
800,
self._process_request_completion
)
def register_provider( def register_provider(
self, self,
provider_name: str, provider_name: str,
provider: GtkSource.CompletionProvider, provider: GtkSource.CompletionProvider,
priority: int = 0, language_ids: list = []
language_ids: list = None
): ):
"""Register completion providers with priority and language filtering""" self._providers[provider_name] = provider
...
for completer in self._completers:
completer.add_provider(provider)
def unregister_provider(self, provider_name: str): def unregister_provider(self, provider_name: str):
"""Remove completion providers""" provider = self._providers[provider_name]
... del self._providers[provider_name]
def get_active_providers(self, language_id: str = None) -> list: for completer in self._completers:
"""Get providers filtered by language""" completer.remove_provider(provider)
...
def provider_process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
self.words_provider.register(event.file.buffer)
for provider in self._providers.values():
provider.response_cache.process_file_load(event)
def provider_process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
self.words_provider.unregister(event.file.buffer)
for provider in self._providers.values():
provider.response_cache.process_file_close(event)
def provider_process_file_save(self, event: Code_Event_Types.SavedFileEvent):
for provider in self._providers.values():
provider.response_cache.process_file_save(event)
def provider_process_file_change(self, event: Code_Event_Types.TextChangedEvent):
for provider in self._providers.values():
provider.response_cache.process_file_change(event)
def request_unbound_completion(self, completer: GtkSource.Completion):
completer.start(
[ *self._providers.values() ],
completer.create_context()
)

View File

@@ -3,13 +3,12 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code import CodeEvent from libs.controllers.controller_base import ControllerBase
from ..event_factory import Event_Factory, Event_Factory_Types from libs.event_factory import Event_Factory, Code_Event_Types
from ..source_file import SourceFile from ..source_file import SourceFile
from ..source_buffer import SourceBuffer from ..source_buffer import SourceBuffer
from .controller_base import ControllerBase
@@ -18,21 +17,21 @@ class FilesController(ControllerBase, list):
super(FilesController, self).__init__() super(FilesController, self).__init__()
def _controller_message(self, event: CodeEvent): def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Event_Factory_Types.AddNewFileEvent): if isinstance(event, Code_Event_Types.AddNewFileEvent):
self.new_file(event) self.new_file(event)
elif isinstance(event, Event_Factory_Types.SwapFileEvent): elif isinstance(event, Code_Event_Types.SwapFileEvent):
self.swap_file(event) self.swap_file(event)
elif isinstance(event, Event_Factory_Types.PopFileEvent): elif isinstance(event, Code_Event_Types.PopFileEvent):
self.pop_file(event) self.pop_file(event)
elif isinstance(event, Event_Factory_Types.RemoveFileEvent): elif isinstance(event, Code_Event_Types.RemoveFileEvent):
self.remove_file(event) self.remove_file(event)
elif isinstance(event, Event_Factory_Types.GetFileEvent): elif isinstance(event, Code_Event_Types.GetFileEvent):
self.get_file(event) self.get_file(event)
elif isinstance(event, Event_Factory_Types.GetSwapFileEvent): elif isinstance(event, Code_Event_Types.GetSwapFileEvent):
self.get_swap_file(event) self.get_swap_file(event)
def get_file(self, event: Event_Factory_Types.GetFileEvent): def get_file(self, event: Code_Event_Types.GetFileEvent):
if not event.buffer: return if not event.buffer: return
for file in self: for file in self:
@@ -42,7 +41,7 @@ class FilesController(ControllerBase, list):
return file return file
def get_swap_file(self, event: Event_Factory_Types.GetSwapFileEvent): def get_swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
if not event.buffer: return if not event.buffer: return
for i, file in enumerate(self): for i, file in enumerate(self):
@@ -56,7 +55,7 @@ class FilesController(ControllerBase, list):
return swapped_file, next_file return swapped_file, next_file
def new_file(self, event: Event_Factory_Types.AddNewFileEvent): def new_file(self, event: Code_Event_Types.AddNewFileEvent):
file = SourceFile() file = SourceFile()
file.emit = self.emit file.emit = self.emit
file.emit_to = self.emit_to file.emit_to = self.emit_to
@@ -68,13 +67,13 @@ class FilesController(ControllerBase, list):
view = event.view, view = event.view,
file = file file = file
) )
self.message_all(eve) self.message(eve)
self.append(file) self.append(file)
return file return file
def swap_file(self, event: Event_Factory_Types.GetSwapFileEvent): def swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
if not event.buffer: return if not event.buffer: return
for i, file in enumerate(self): for i, file in enumerate(self):
@@ -88,7 +87,7 @@ class FilesController(ControllerBase, list):
return swapped_file, next_file return swapped_file, next_file
def pop_file(self, event: Event_Factory_Types.PopFileEvent): def pop_file(self, event: Code_Event_Types.PopFileEvent):
if not event.buffer: return if not event.buffer: return
for i, file in enumerate(self): for i, file in enumerate(self):
@@ -106,11 +105,11 @@ class FilesController(ControllerBase, list):
file = popped_file, file = popped_file,
next_file = next_file next_file = next_file
) )
self.message_all(eve) self.message(eve)
return popped_file, next_file return popped_file, next_file
def remove_file(self, event: Event_Factory_Types.RemoveFileEvent): def remove_file(self, event: Code_Event_Types.RemoveFileEvent):
if not event.buffer: return if not event.buffer: return
for i, file in enumerate(self): for i, file in enumerate(self):
@@ -128,7 +127,7 @@ class FilesController(ControllerBase, list):
file = file, file = file,
next_file = next_file next_file = next_file
) )
self.message_all(eve) self.message(eve)
self.remove(file) self.remove(file)
file.close() file.close()

View File

@@ -1,128 +0,0 @@
# Python imports
# Lib imports
# Application imports
from ..event_factory import Event_Factory, Event_Factory_Types
from ..command_system import CommandSystem
from ..key_mapper import KeyMapper
from ..source_view import SourceView
from .controller_base import ControllerBase
class SourceViewsController(ControllerBase, list):
def __init__(self):
super(SourceViewsController, self).__init__()
self.key_mapper: KeyMapper = KeyMapper()
self.active_view: SourceView = None
def get_command_system(self):
event = Event_Factory.create_event("get_command_system")
self.message_to("commands", event)
command = event.response
del event
return command
def create_source_view(self):
source_view: SourceView = SourceView()
source_view.command = self.get_command_system()
source_view.command.set_data(source_view)
self._map_signals(source_view)
self.append(source_view)
return source_view
def _controller_message(self, event: Event_Factory_Types.CodeEvent):
if isinstance(event, Event_Factory_Types.RemovedFileEvent):
self._remove_file(event)
elif isinstance(event, Event_Factory_Types.TextChangedEvent):
self.active_view.command.exec("update_info_bar")
def _map_signals(self, source_view: SourceView):
source_view.connect("focus-in-event", self._focus_in_event)
source_view.connect("move-cursor", self._move_cursor)
source_view.connect("key-press-event", self._key_press_event)
source_view.connect("key-release-event", self._key_release_event)
source_view.connect("button-press-event", self._button_press_event)
source_view.connect("button-release-event", self._button_release_event)
def _focus_in_event(self, view, eve):
self.active_view = view
view.command.exec("set_miniview")
view.command.exec("set_focus_border")
view.command.exec("update_info_bar")
event = Event_Factory.create_focused_view(view = view)
self.emit(event)
def _move_cursor(self, view, step, count, extend_selection):
buffer = view.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line()
char = iter.get_line_offset()
event = Event_Factory.create_cursor_moved(
view = view,
buffer = buffer,
line = line,
char = char
)
self.emit(event)
view.command.exec("update_info_bar")
def _button_press_event(self, view, eve):
self.active_view.command.exec("update_info_bar")
def _button_release_event(self, view, eve):
self.active_view.command.exec("update_info_bar")
def _key_press_event(self, view, eve):
command = self.key_mapper._key_press_event(eve)
is_future = self.key_mapper._key_release_event(eve)
if is_future: return True
if not command: return False
view.command.exec(command)
return True
def _key_release_event(self, view, eve):
command = self.key_mapper._key_release_event(eve)
is_past = self.key_mapper._key_press_event(eve)
if is_past: return True
if not command: return False
view.command.exec(command)
return True
def _remove_file(self, event: Event_Factory_Types.RemovedFileEvent):
for view in self:
if not event.file.buffer == view.get_buffer(): continue
if not event.next_file:
view.command.exec("new_file")
continue
view.set_buffer(event.next_file.buffer)
def first_map_load(self):
for view in self:
view.command.exec("new_file")
view = self[0]
view.grab_focus()
view.command.exec("load_start_files")

View File

@@ -3,16 +3,14 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code import CodeEvent from libs.controllers.controller_base import ControllerBase
from ..event_factory import Event_Factory, Event_Factory_Types from libs.event_factory import Event_Factory, Code_Event_Types
from ..tabs_widget import TabsWidget from ..tabs_widget import TabsWidget
from ..tab_widget import TabWidget from ..tab_widget import TabWidget
from ..source_view import SourceView from ..source_view import SourceView
from .controller_base import ControllerBase
class TabsController(ControllerBase): class TabsController(ControllerBase):
@@ -23,28 +21,28 @@ class TabsController(ControllerBase):
self.tabs_widget: TabsWidget = TabsWidget() self.tabs_widget: TabsWidget = TabsWidget()
def _controller_message(self, event: CodeEvent): def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Event_Factory_Types.FocusedViewEvent): if isinstance(event, Code_Event_Types.FocusedViewEvent):
self.active_view = event.view self.active_view = event.view
elif isinstance(event, Event_Factory_Types.FilePathSetEvent): elif isinstance(event, Code_Event_Types.FilePathSetEvent):
self.update_tab_label(event) self.update_tab_label(event)
elif isinstance(event, Event_Factory_Types.AddedNewFileEvent): elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.add_tab(event) self.add_tab(event)
elif isinstance(event, Event_Factory_Types.PoppedFileEvent): elif isinstance(event, Code_Event_Types.PoppedFileEvent):
... ...
elif isinstance(event, Event_Factory_Types.RemovedFileEvent): elif isinstance(event, Code_Event_Types.RemovedFileEvent):
self.remove_tab(event) self.remove_tab(event)
def get_tabs_widget(self): def get_tabs_widget(self):
return self.tabs_widget return self.tabs_widget
def update_tab_label(self, event: Event_Factory_Types.FilePathSetEvent): def update_tab_label(self, event: Code_Event_Types.FilePathSetEvent):
for tab in self.tabs_widget.get_children(): for tab in self.tabs_widget.get_children():
if not event.file == tab.file: continue if not event.file == tab.file: continue
tab.label.set_label(event.file.fname) tab.label.set_label(event.file.fname)
break break
def add_tab(self, event: Event_Factory_Types.AddedNewFileEvent): def add_tab(self, event: Code_Event_Types.AddedNewFileEvent):
def set_active_tab(tab, eve, file): def set_active_tab(tab, eve, file):
event = Event_Factory.create_event( event = Event_Factory.create_event(
"set_active_file", "set_active_file",
@@ -55,7 +53,7 @@ class TabsController(ControllerBase):
tab.get_parent().file.buffer tab.get_parent().file.buffer
) )
self.message_all(event) self.message(event)
def close_tab(tab, eve, file): def close_tab(tab, eve, file):
event = Event_Factory.create_event( event = Event_Factory.create_event(
@@ -63,7 +61,7 @@ class TabsController(ControllerBase):
buffer = tab.get_parent().file.buffer buffer = tab.get_parent().file.buffer
) )
self.message_all(event) self.message(event)
tab = TabWidget() tab = TabWidget()
tab.file = event.file tab.file = event.file
@@ -74,7 +72,7 @@ class TabsController(ControllerBase):
self.tabs_widget.add(tab) self.tabs_widget.add(tab)
tab.show() tab.show()
def remove_tab(self, event: Event_Factory_Types.RemovedFileEvent): def remove_tab(self, event: Code_Event_Types.RemovedFileEvent):
for tab in self.tabs_widget.get_children(): for tab in self.tabs_widget.get_children():
if not event.file == tab.file: continue if not event.file == tab.file: continue

View File

@@ -0,0 +1,10 @@
"""
Code Controllers Package
"""
from .state_manager import SourceViewStateManager
from .signal_mapper import SourceViewSignalMapper
from .source_views_controller import SourceViewsController
# State imports
from .states import *

View File

@@ -0,0 +1,63 @@
# Python imports
# Lib imports
# Application imports
from ...source_view import SourceView
class SourceViewSignalMapper:
def __init__(self):
self.active_view: SourceView = None
def bind_emit(self, emit: callable):
self.emit = emit
def set_state_manager(self, state_manager):
self.state_manager = state_manager
def connect_signals(self, source_view: SourceView):
signal_mappings = self._get_signal_mappings()
for signal, handler in signal_mappings.items():
source_view.connect(signal, handler)
def disconnect_signals(self, source_view: SourceView):
signal_mappings = self._get_signal_mappings()
for signal, handler in signal_mappings.items():
source_view.disconnect_by_func(handler)
def insert_text(self, file, string: str):
return self.state_manager.handle_insert_text(self.active_view, file, string)
def _get_signal_mappings(self):
return {
"focus-in-event": self._focus_in_event,
"move-cursor": self._move_cursor,
"key-press-event": self._key_press_event,
"key-release-event": self._key_release_event,
"button-press-event": self._button_press_event,
"button-release-event": self._button_release_event
}
def _focus_in_event(self, source_view: SourceView, eve):
self.active_view = source_view
return self.state_manager.handle_focus_in_event(source_view, eve, self.emit)
def _move_cursor(self, source_view: SourceView, step, count, extend_selection):
return self.state_manager.handle_move_cursor(
source_view, step, count, extend_selection, self.emit
)
def _button_press_event(self, source_view: SourceView, eve):
return self.state_manager.handle_button_press_event(source_view, eve)
def _button_release_event(self, source_view: SourceView, eve):
return self.state_manager.handle_button_release_event(source_view, eve)
def _key_press_event(self, source_view: SourceView, eve):
return self.state_manager.handle_key_press_event(source_view, eve)
def _key_release_event(self, source_view: SourceView, eve):
return self.state_manager.handle_key_release_event(source_view, eve)

View File

@@ -0,0 +1,74 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
from ...source_view import SourceView
from .state_manager import SourceViewStateManager
from .signal_mapper import SourceViewSignalMapper
class SourceViewsController(ControllerBase, list):
def __init__(self):
super(SourceViewsController, self).__init__()
self.state_manager: SourceViewStateManager = SourceViewStateManager()
self.signal_mapper: SourceViewSignalMapper = SourceViewSignalMapper()
self.signal_mapper.bind_emit(self.emit)
self.signal_mapper.set_state_manager(self.state_manager)
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.RemovedFileEvent):
self._remove_file(event)
if not self.signal_mapper.active_view: return
if isinstance(event, Code_Event_Types.TextChangedEvent):
if not self.signal_mapper.active_view: return
self.signal_mapper.active_view.command.exec("update_info_bar")
elif isinstance(event, Code_Event_Types.TextChangedEvent):
self.signal_mapper.active_view.command.exec("update_info_bar")
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
self.signal_mapper.insert_text(event.file, event.text)
def _get_command_system(self):
event = Event_Factory.create_event("get_command_system")
self.message_to("commands", event)
command = event.response
del event
return command
def _remove_file(self, event: Code_Event_Types.RemovedFileEvent):
for source_view in self:
if not event.file.buffer == source_view.get_buffer(): continue
if not event.next_file:
source_view.command.exec("new_file")
continue
source_view.set_buffer(event.next_file.buffer)
def create_source_view(self):
source_view: SourceView = SourceView()
source_view.command = self._get_command_system()
source_view.command.set_data(source_view)
self.signal_mapper.connect_signals(source_view)
self.append(source_view)
return source_view
def first_map_load(self):
for source_view in self:
source_view.command.exec("new_file")
source_view = self[0]
source_view.grab_focus()
source_view.command.exec("load_start_files")

View File

@@ -0,0 +1,65 @@
# Python imports
# Lib imports
# Application imports
from libs.dto.states import SourceViewStates
from ...key_mapper import KeyMapper
from .states import *
class SourceViewStateManager:
def __init__(self):
self.key_mapper: KeyMapper = KeyMapper()
self.states: dict = {
SourceViewStates.INSERT: SourceViewsInsertState(),
SourceViewStates.MULTIINSERT: SourceViewsMultiInsertState(),
SourceViewStates.COMMAND: SourceViewsCommandState(),
SourceViewStates.READONLY: SourceViewsReadOnlyState()
}
def handle_focus_in_event(self, source_view, eve, emit):
return self.states[source_view.state].focus_in_event(source_view, eve, emit)
def handle_insert_text(self, source_view, file, text):
return self.states[source_view.state].insert_text(file, text)
def handle_move_cursor(self, source_view, step, count, extend_selection, emit):
return self.states[source_view.state].move_cursor(
source_view, step, count, extend_selection, emit
)
def handle_button_press_event(self, source_view, eve):
return self.states[source_view.state].button_press_event(source_view, eve)
def handle_button_release_event(self, source_view, eve):
# Handle state transitions (multi-insert toggling)
self._handle_multi_insert_toggle(source_view, eve)
return self.states[source_view.state].button_release_event(source_view, eve)
def handle_key_press_event(self, source_view, eve):
return self.states[source_view.state].key_press_event(
source_view, eve, self.key_mapper
)
def handle_key_release_event(self, source_view, eve):
return self.states[source_view.state].key_release_event(
source_view, eve, self.key_mapper
)
def _handle_multi_insert_toggle(self, source_view, eve):
is_control = self.key_mapper.is_control(eve)
if is_control and not source_view.state == SourceViewStates.MULTIINSERT:
logger.debug("Entered Multi-Insert Mode...")
source_view.state = SourceViewStates.MULTIINSERT
if not is_control and source_view.state == SourceViewStates.MULTIINSERT:
logger.debug("Entered Regular Insert Mode...")
self.states[source_view.state].clear_markers(source_view)
source_view.state = SourceViewStates.INSERT

View File

@@ -0,0 +1,8 @@
"""
Code Controllers Views States Package
"""
from .source_view_insert_state import SourceViewsInsertState
from .source_view_multi_insert_state import SourceViewsMultiInsertState
from .source_view_command_state import SourceViewsCommandState
from .source_view_read_only_state import SourceViewsReadOnlyState

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsCommandState:
def __init__(self):
super(SourceViewsCommandState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
return True
def insert_text(self, file, text):
return True
def button_press_event(self, source_view, eve):
return True
def button_release_event(self, source_view, eve):
return True
def key_press_event(self, source_view, eve, key_mapper):
return True
def key_release_event(self, source_view, eve, key_mapper):
return True

View File

@@ -0,0 +1,71 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsInsertState:
def __init__(self):
super(SourceViewsInsertState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
source_view.command.exec("set_miniview")
source_view.command.exec("set_focus_border")
source_view.command.exec("update_info_bar")
event = Event_Factory.create_event("focused_view", view = source_view)
emit(event)
def insert_text(self, file, text):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
buffer = source_view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
line = itr.get_line()
char = itr.get_line_offset()
event = Event_Factory.create_event(
"cursor_moved",
view = source_view,
buffer = buffer,
line = line,
char = char
)
emit(event)
source_view.command.exec("update_info_bar")
def button_press_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
def button_release_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
def key_press_event(self, source_view, eve, key_mapper):
command = key_mapper._key_press_event(eve)
is_future = key_mapper._key_release_event(eve)
if is_future: return True
if not command: return False
source_view.command.exec(command)
return True
def key_release_event(self, source_view, eve, key_mapper):
command = key_mapper._key_release_event(eve)
is_past = key_mapper._key_press_event(eve)
if is_past: return True
if not command: return False
source_view.command.exec(command)
return True

View File

@@ -0,0 +1,131 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
from ....mixins.source_mark_events_mixin import MarkEventsMixin
class SourceViewsMultiInsertState(MarkEventsMixin):
def __init__(self):
super(SourceViewsMultiInsertState, self).__init__()
self.cursor_action: CursorAction = 0
self.move_direction: MoveDirection = 0
self.insert_markers: list = []
def focus_in_event(self, source_view, eve, emit):
source_view.command.exec("set_miniview")
source_view.command.exec("set_focus_border")
source_view.command.exec("update_info_bar")
event = Event_Factory.create_event("focused_view", view = source_view)
emit(event)
def insert_text(self, file, text):
if not self.insert_markers: return False
buffer = file.buffer
# freeze buffer and insert to each mark (if any)
buffer.block_insert_after_signal()
buffer.begin_user_action()
with buffer.freeze_notify():
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
buffer.insert(itr, text, -1)
buffer.end_user_action()
buffer.unblock_insert_after_signal()
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
buffer = source_view.get_buffer()
self._process_move_direction(buffer)
self._signal_cursor_moved(source_view, emit)
source_view.command.exec("update_info_bar")
def button_press_event(self, source_view, eve):
source_view.command.exec("update_info_bar")
return True
def button_release_event(self, source_view, eve):
buffer = source_view.get_buffer()
insert_iter = buffer.get_iter_at_mark( buffer.get_insert() )
data = source_view.window_to_buffer_coords(
Gtk.TextWindowType.TEXT,
eve.x,
eve.y
)
is_over_text, \
target_iter, \
is_trailing = source_view.get_iter_at_position(data.buffer_x, data.buffer_y)
if not is_over_text:
# NOTE: Trying to put at very end of line if not over text (aka, clicking right of text)
target_iter.forward_visible_line()
target_iter.backward_char()
self._insert_mark(insert_iter, target_iter, buffer)
def key_press_event(self, source_view, eve, key_mapper):
char = key_mapper.get_raw_keyname(eve)
for action in CursorAction:
if not action.name == char.upper(): continue
self.cursor_action = action.value
self._process_cursor_action(source_view.get_buffer())
return False
for direction in MoveDirection:
if not direction.name == char.upper(): continue
self.move_direction = direction.value
return False
is_future = key_mapper._key_release_event(eve)
if is_future: return False
command = key_mapper._key_press_event(eve)
if not command: return False
source_view.command.exec(command)
return True
def key_release_event(self, source_view, eve, key_mapper):
command = key_mapper._key_release_event(eve)
is_past = key_mapper._key_press_event(eve)
if is_past: return False
if not command: return False
source_view.command.exec(command)
return True
def _signal_cursor_moved(self, source_view, emit):
buffer = source_view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
line = itr.get_line()
char = itr.get_line_offset()
event = Event_Factory.create_event(
"cursor_moved",
view = source_view,
buffer = buffer,
line = line,
char = char
)
emit(event)

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from libs.dto.states import SourceViewStates
class SourceViewsReadOnlyState:
def __init__(self):
super(SourceViewsReadOnlyState, self).__init__()
def focus_in_event(self, source_view, eve, emit):
return True
def move_cursor(self, source_view, step, count, extend_selection, emit):
return True
def insert_text(self, file, text):
return True
def button_press_event(self, source_view, eve):
return True
def button_release_event(self, source_view, eve):
return True
def key_press_event(self, source_view, eve, key_mapper):
return True
def key_release_event(self, source_view, eve, key_mapper):
return True

View File

@@ -1,101 +0,0 @@
# Python imports
import inspect
from typing import Dict, Type
import re
# Lib imports
# Application imports
from libs.singleton import Singleton
from libs.dto.code import CodeEvent
from libs.dto import code
class EventFactory(Singleton):
def __init__(self):
self._event_classes: Dict[str, Type[CodeEvent]] = {}
self._auto_register_events()
def register_event(self, event_type: str, event_class: Type[CodeEvent]):
self._event_classes[event_type] = event_class
def create_event(self, event_type: str, **kwargs) -> CodeEvent:
if event_type not in self._event_classes:
raise ValueError(f"Unknown event type: {event_type}")
event_class = self._event_classes[event_type]
event = event_class()
for key, value in kwargs.items():
if hasattr(event, key):
setattr(event, key, value)
else:
raise ValueError(f"Event class {event_class.__name__} has no attribute '{key}'")
return event
def _auto_register_events(self):
for name, obj in code.__dict__.items():
if not self._is_valid_event_class(obj): continue
event_type = self._class_name_to_event_type(name)
self.register_event(event_type, obj)
logger.debug(f"Auto-registered {len(self._event_classes)} event types")
def _is_valid_event_class(self, obj) -> bool:
return (inspect.isclass(obj) and
issubclass(obj, CodeEvent) and
obj != CodeEvent)
def _class_name_to_event_type(self, class_name: str) -> str:
base_name = class_name[:-5] if class_name.endswith('Event') else class_name
return re.sub(r'(?<!^)(?=[A-Z])', '_', base_name).lower()
def create_cursor_moved(self, **kwargs):
return self.create_event("cursor_moved", **kwargs)
def create_text_changed(self, **kwargs):
return self.create_event("text_changed", **kwargs)
def create_focused_view(self, **kwargs):
return self.create_event("focused_view", **kwargs)
def create_modified_changed(self, **kwargs):
return self.create_event("modified_changed", **kwargs)
def create_get_command_system(self, **kwargs):
return self.create_event("get_command_system", **kwargs)
def create_file_path_set(self, **kwargs):
return self.create_event("file_path_set", **kwargs)
def create_text_inserted(self, **kwargs):
return self.create_event("text_inserted", **kwargs)
def create_set_active_file(self, **kwargs):
return self.create_event("set_active_file", **kwargs)
def create_added_new_file(self, **kwargs):
return self.create_event("added_new_file", **kwargs)
def create_popped_file(self, **kwargs):
return self.create_event("popped_file", **kwargs)
def create_get_file(self, **kwargs):
return self.create_event("get_file", **kwargs)
def create_get_swap_file(self, **kwargs):
return self.create_event("get_swap_file", **kwargs)
def create_remove_file(self, **kwargs):
return self.create_event("remove_file", **kwargs)
def create_removed_file(self, **kwargs):
return self.create_event("removed_file", **kwargs)
Event_Factory = EventFactory()
Event_Factory_Types = code

View File

@@ -125,3 +125,13 @@ class KeyMapper:
if is_alt: if is_alt:
self.state = self.state | AltKeyState self.state = self.state | AltKeyState
def is_control(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
return True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
def is_shift(self, eve):
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
return True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
def get_raw_keyname(self, eve):
return Gdk.keyval_name(eve.keyval)

View File

@@ -0,0 +1,107 @@
# Python imports
import random
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
class MarkEventsMixin:
def clear_markers(self, source_view):
buffer = source_view.get_buffer()
for mark in self.insert_markers:
mark.set_visible(False)
buffer.delete_mark(mark)
self.insert_markers.clear()
def _insert_mark(self, insert_iter, target_iter, buffer):
mark_found = self._check_for_insert_marks(target_iter, buffer)
if mark_found: return
random_bits = random.getrandbits(128)
hash = "%032x" % random_bits
mark = Gtk.TextMark.new(
name = f"multi_insert_{hash}",
left_gravity = False
)
buffer.add_mark(mark, target_iter)
mark.set_visible(True)
self.insert_markers.append(mark)
def _check_for_insert_marks(self, target_iter, buffer):
marks = target_iter.get_marks()
for mark in marks:
if mark in self.insert_markers[:]:
mark.set_visible(False)
self.insert_markers.remove(mark)
buffer.delete_mark(mark)
return True
insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
if target_iter.equal(insert_itr): return True
return False
def _process_cursor_action(self, buffer):
if not self.insert_markers: return
if self.cursor_action == CursorAction.NONE.value: return
action = self.cursor_action
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
if action == CursorAction.BACKSPACE.value:
buffer.backspace(itr, interactive = True, default_editable = True)
elif action == CursorAction.DELETE.value:
itr.forward_char()
buffer.backspace(itr, interactive = True, default_editable = True)
elif action == CursorAction.ENTER.value:
...
self.cursor_action = CursorAction.NONE.value
def _process_move_direction(self, buffer):
if not self.insert_markers: return
if self.move_direction == MoveDirection.NONE.value: return
direction = self.move_direction
for mark in self.insert_markers:
itr = buffer.get_iter_at_mark(mark)
if direction == MoveDirection.UP.value:
new_line = itr.get_line() - 1
new_itr = buffer.get_iter_at_line_offset(
new_line,
itr.get_line_index()
)
elif direction == MoveDirection.DOWN.value:
new_line = itr.get_line() + 1
new_itr = buffer.get_iter_at_line_offset(
new_line,
itr.get_line_index()
)
elif direction == MoveDirection.LEFT.value:
if not itr.backward_char(): break
new_itr = itr
elif direction == MoveDirection.RIGHT.value:
if not itr.forward_char(): break
new_itr = itr
else:
continue
buffer.move_mark_by_name(mark.get_name(), new_itr)
self.move_direction = MoveDirection.NONE

View File

@@ -21,20 +21,41 @@ class SourceBuffer(GtkSource.Buffer):
_changed, _changed,
_mark_set, _mark_set,
_insert_text, _insert_text,
_after_insert_text,
_modified_changed, _modified_changed,
): ):
self._handler_ids = [ self._handler_ids = [
self.connect("changed", _changed), self.connect("changed", _changed),
self.connect("mark-set", _mark_set), self.connect("mark-set", _mark_set),
self.connect("insert-text", _insert_text), self.connect("insert-text", _insert_text),
self.connect("modified-changed", _modified_changed) self.connect_after("insert-text", _after_insert_text),
self.connect("modified-changed", _modified_changed)
] ]
def block_changed_signal(self):
self.handler_block(self._handler_ids[0])
def block_insert_after_signal(self):
self.handler_block(self._handler_ids[3])
def block_modified_changed_signal(self):
self.handler_block(self._handler_ids[4])
def unblock_changed_signal(self):
self.handler_unblock(self._handler_ids[0])
def unblock_insert_after_signal(self):
self.handler_unblock(self._handler_ids[3])
def unblock_modified_changed_signal(self):
self.handler_unblock(self._handler_ids[4])
def clear_signals(self): def clear_signals(self):
for handle_id in self._handler_ids: for handle_id in self._handler_ids:
self.disconnect(handle_id) self.disconnect(handle_id)
def __del__(self): def __del__(self):
for handle_id in self._handler_ids: for handle_id in self._handler_ids:
self.disconnect(handle_id) self.disconnect(handle_id)

View File

@@ -8,12 +8,12 @@ gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GtkSource from gi.repository import GtkSource
from gi.repository import Gio from gi.repository import Gio
# Application imports # Application imports
from libs.dto.code import CodeEvent from libs.event_factory import Event_Factory, Code_Event_Types
from .event_factory import Event_Factory, Event_Factory_Types
from .source_buffer import SourceBuffer from .source_buffer import SourceBuffer
@@ -38,37 +38,60 @@ class SourceFile(GtkSource.File):
self._changed, self._changed,
self._mark_set, self._mark_set,
self._insert_text, self._insert_text,
self._after_insert_text,
self._modified_changed self._modified_changed
) )
def _changed(self, buffer: SourceBuffer): def _changed(self, buffer: SourceBuffer):
event = Event_Factory.create_text_changed(buffer = buffer) event = Event_Factory.create_event("text_changed", buffer = buffer)
event.file = self event.file = self
self.emit(event) self.emit(event)
def _insert_text(self, buffer: SourceBuffer, location: Gtk.TextIter, def _insert_text(
self,
buffer: SourceBuffer,
location: Gtk.TextIter,
text: str, length: int
):
...
def _after_insert_text(
self,
buffer: SourceBuffer,
location: Gtk.TextIter,
text: str, length: int text: str, length: int
): ):
event = Event_Factory.create_event( event = Event_Factory.create_event(
"text_inserted", "text_inserted",
file = self, file = self,
buffer = buffer buffer = self.buffer,
location = location,
text = text,
length = length
) )
self.emit(event)
def _mark_set(self, buffer: SourceBuffer, location: Gtk.TextIter, # Note: 'idle_add' needed b/c markers don't get thir positions
# updated relative to the initial insert.
# If not used, seg faults galor during multi insert.
GLib.idle_add(self.emit, event)
def _mark_set(
self,
buffer: SourceBuffer,
location: Gtk.TextIter,
mark: Gtk.TextMark mark: Gtk.TextMark
): ):
# event = CodeEvent() # event = Event_Factory.create_event(
# event.etype = "mark_set" # "mark_set",
# event.file = self # file = self, buffer = buffer
# event.buffer = buffer # )
# self.emit(event) # self.emit(event)
... ...
def _modified_changed(self, buffer: SourceBuffer): def _modified_changed(self, buffer: SourceBuffer):
event = Event_Factory.create_modified_changed( event = Event_Factory.create_event(
"modified_changed",
file = self, buffer = buffer file = self, buffer = buffer
) )
@@ -108,6 +131,12 @@ class SourceFile(GtkSource.File):
self.emit(event) self.emit(event)
def save(self): def save(self):
event = Event_Factory.create_event(
"saved_file",
file = self, buffer = self.buffer
)
self.emit(event)
self._write_file( self.get_location() ) self._write_file( self.get_location() )
def save_as(self): def save_as(self):
@@ -122,8 +151,8 @@ class SourceFile(GtkSource.File):
def close(self): def close(self):
del self.buffer del self.buffer
def emit(self, event: CodeEvent): def emit(self, event: Code_Event_Types.CodeEvent):
... ...
def emit_to(self, controller: str, event: CodeEvent): def emit_to(self, controller: str, event: Code_Event_Types.CodeEvent):
... ...

View File

@@ -9,14 +9,19 @@ from gi.repository import Gtk
from gi.repository import GLib from gi.repository import GLib
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports
from libs.dto.states import SourceViewStates
from .mixins.source_view_dnd_mixin import SourceViewDnDMixin from .mixins.source_view_dnd_mixin import SourceViewDnDMixin
class SourceView(GtkSource.View, SourceViewDnDMixin): class SourceView(GtkSource.View, SourceViewDnDMixin):
def __init__(self): def __init__(self, state: SourceViewStates = SourceViewStates.INSERT):
super(SourceView, self).__init__() super(SourceView, self).__init__()
self.state = state
self._cut_temp_timeout_id = None self._cut_temp_timeout_id = None
self._cut_buffer = "" self._cut_buffer = ""

View File

@@ -6,7 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from libs.dto.code.code_event import CodeEvent from libs.event_factory import Code_Event_Types
from .source_view import SourceView from .source_view import SourceView
from .source_file import SourceFile from .source_file import SourceFile
@@ -37,7 +37,7 @@ class TabsWidget(Gtk.ButtonBox):
def _load_widgets(self): def _load_widgets(self):
... ...
def add_tab(self, event: CodeEvent): def add_tab(self, event: Code_Event_Types.CodeEvent):
"""Add a tab widget for the given file event.""" """Add a tab widget for the given file event."""
if not hasattr(self, 'tabs'): if not hasattr(self, 'tabs'):
return return
@@ -61,7 +61,7 @@ class TabsWidget(Gtk.ButtonBox):
self.tabs.add(tab) self.tabs.add(tab)
def remove_tab(self, event: CodeEvent): def remove_tab(self, event: Code_Event_Types.CodeEvent):
"""Remove a tab widget for the given file event.""" """Remove a tab widget for the given file event."""
if not hasattr(self, 'tabs'): if not hasattr(self, 'tabs'):
return return
@@ -75,7 +75,7 @@ class TabsWidget(Gtk.ButtonBox):
return return
def update_tab_label(self, event: CodeEvent): def update_tab_label(self, event: Code_Event_Types.CodeEvent):
"""Update tab label for the given file event.""" """Update tab label for the given file event."""
if not hasattr(self, 'tabs'): if not hasattr(self, 'tabs'):
return return

View File

@@ -13,9 +13,8 @@ class Separator(Gtk.Separator):
def __init__(self, id: str = None, ORIENTATION: int = 0): def __init__(self, id: str = None, ORIENTATION: int = 0):
super(Separator, self).__init__() super(Separator, self).__init__()
builder = settings_manager.get_builder()
if id: if id:
builder.expose_object(id, self) widget_registery.expose_object(id, self)
self.ORIENTATION = ORIENTATION self.ORIENTATION = ORIENTATION
self._setup_styling() self._setup_styling()

View File

@@ -12,7 +12,6 @@ from gi.repository import GLib
from gi.repository import Vte from gi.repository import Vte
# Application imports # Application imports
from libs.dto.event import Event
@@ -59,28 +58,29 @@ class VteWidget(Vte.Terminal):
... ...
def _do_session_spawn(self): def _do_session_spawn(self):
env = [
"DISPLAY=:0",
"LC_ALL=C",
"TERM='xterm-256color'",
f"HOME='{settings_manager.path_manager.get_home_path()}'",
"XDG_RUNTIME_DIR='/run/user/1000'",
f"XAUTHORITY='{settings_manager.path_manager.get_home_path()}/.Xauthority'",
"HISTFILE=/dev/null",
"HISTSIZE=0",
"HISTFILESIZE=0",
"PS1=\\h@\\u \\W -->: ",
]
self.spawn_sync( self.spawn_sync(
Vte.PtyFlags.DEFAULT, Vte.PtyFlags.DEFAULT,
settings_manager.path_manager.get_home_path(), settings_manager.path_manager.get_home_path(),
["/bin/bash"], ["/bin/bash"],
[], env,
GLib.SpawnFlags.DEFAULT, GLib.SpawnFlags.DEFAULT,
None, None, None, None,
) )
# Note: '-->:' is used as a delimiter to split on to get command actual.
# !!! DO NOT REMOVE UNLESS CODE UPDATED ACCORDINGLY !!!
# Also, KEEP the <space> prefix in commands to keep from inserting to bash history.
startup_cmds = [ startup_cmds = [
" env -i /bin/bash --noprofile --norc\n",
" export TERM='xterm-256color'\n",
" export LC_ALL=C\n",
" export XDG_RUNTIME_DIR='/run/user/1000'\n",
" export DISPLAY=:0\n",
f" export XAUTHORITY='{settings_manager.path_manager.get_home_path()}/.Xauthority'\n",
f" \nexport HOME='{settings_manager.path_manager.get_home_path()}'\n",
" export PS1='\\h@\\u \\W -->: '\n",
" clear\n"
] ]
for i in startup_cmds: for i in startup_cmds:

View File

@@ -10,7 +10,7 @@ from gi.repository import WebKit2
# Application imports # Application imports
from libs.settings.other.webkit_ui_settings import WebkitUISettings from libs.settings.other.webkit_ui_settings import WebkitUISettings
from libs.dto.event import Event from libs.dto.base_event import BaseEvent
class WebkitUI(WebKit2.WebView): class WebkitUI(WebKit2.WebView):
@@ -44,6 +44,7 @@ class WebkitUI(WebKit2.WebView):
data = f.read() data = f.read()
self.load_html(content = data, base_uri = f"file://{path}/") self.load_html(content = data, base_uri = f"file://{path}/")
# self.load_uri("https://duckduckgo.com/")
def _setup_content_manager(self): def _setup_content_manager(self):
content_manager = self.get_user_content_manager() content_manager = self.get_user_content_manager()
@@ -55,7 +56,7 @@ class WebkitUI(WebKit2.WebView):
message = js_value.to_string() message = js_value.to_string()
try: try:
event = Event( **json.loads(message) ) event = BaseEvent( **json.loads(message) )
event_system.emit("handle-bridge-event", (event,)) event_system.emit("handle-bridge-event", (event,))
except Exception as e: except Exception as e:
logger.info(e) logger.info(e)

View File

@@ -0,0 +1,3 @@
"""
Libs Controllers Package
"""

View File

@@ -3,9 +3,9 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.singleton import Singleton from ..singleton import Singleton
from libs.dto.code.code_event import CodeEvent from ..dto.base_event import BaseEvent
from .emit_dispatcher import EmitDispatcher from .emit_dispatcher import EmitDispatcher
from .controller_context import ControllerContext from .controller_context import ControllerContext
@@ -24,15 +24,18 @@ class ControllerBase(Singleton, EmitDispatcher):
self.controller_context: ControllerContext = None self.controller_context: ControllerContext = None
def _controller_message(self, event: CodeEvent): def _controller_message(self, event: BaseEvent):
raise ControllerBaseException("Controller Base must override '_controller_message'...") raise ControllerBaseException("Controller Base '_controller_message' must be overridden...")
def set_controller_context(self, controller_context: ControllerContext): def set_controller_context(self, controller_context: ControllerContext):
self.controller_context = controller_context self.controller_context = controller_context
def message_to(self, name: str, event: CodeEvent): def message(self, event: BaseEvent):
return self.controller_context.message(event)
def message_to(self, name: str, event: BaseEvent):
return self.controller_context.message_to(name, event) return self.controller_context.message_to(name, event)
def message_all(self, event: CodeEvent): def message_to_selected(self, names: list[str], event: BaseEvent):
return self.controller_context.message_all(event) for name in names:
self.controller_context.message_to_selected(name, event)

View File

@@ -3,7 +3,7 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code.code_event import CodeEvent from ..dto.base_event import BaseEvent
@@ -17,8 +17,11 @@ class ControllerContext:
super(ControllerContext, self).__init__() super(ControllerContext, self).__init__()
def message_to(self, name: str, event: CodeEvent): def message(self, event: BaseEvent):
raise ControllerContextException("Controller Context 'message' must be overriden by Controller Manager...")
def message_to(self, name: str, event: BaseEvent):
raise ControllerContextException("Controller Context 'message_to' must be overriden by Controller Manager...") raise ControllerContextException("Controller Context 'message_to' must be overriden by Controller Manager...")
def message_all(self, event: CodeEvent): def message_to_selected(self, name: list, event: BaseEvent):
raise ControllerContextException("Controller Context 'message_all' must be overriden by Controller Manager...") raise ControllerContextException("Controller Context 'message_to_selected' must be overriden by Controller Manager...")

View File

@@ -3,9 +3,8 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.singleton import Singleton from ..singleton import Singleton
from ..event_factory import Code_Event_Types
from libs.dto.code.code_event import CodeEvent
from .controller_base import ControllerBase from .controller_base import ControllerBase
from .controller_context import ControllerContext from .controller_context import ControllerContext
@@ -25,7 +24,7 @@ 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_all = self.message_all controller_context.message = self.message
return controller_context return controller_context
@@ -33,16 +32,19 @@ class ControllerManager(Singleton, dict):
if not name or controller == None: if not name or controller == None:
raise ControllerManagerException("Must pass in a 'name' and 'controller'...") raise ControllerManagerException("Must pass in a 'name' and 'controller'...")
controller.set_controller_context( if name in self.keys():
self._crete_controller_context() raise ControllerManagerException(f"Can't bind controller to registered name of '{name}'...")
)
controller.set_controller_context( self._crete_controller_context() )
self[name] = controller self[name] = controller
def get_controllers_key_list(self) -> list[str]:
return self.keys()
def message_to(self, name: str, event: CodeEvent): def message_to(self, name: str, event: Code_Event_Types.CodeEvent):
self[name]._controller_message(event) self[name]._controller_message(event)
def message_all(self, event: CodeEvent): def message(self, event: Code_Event_Types.CodeEvent):
for key in self.keys(): for key in self.keys():
self[key]._controller_message(event) self[key]._controller_message(event)

View File

@@ -3,7 +3,7 @@
# Lib imports # Lib imports
# Application imports # Application imports
from libs.dto.code.code_event import CodeEvent from ..dto.base_event import BaseEvent
@@ -12,8 +12,8 @@ class EmitDispatcher:
super(EmitDispatcher, self).__init__() super(EmitDispatcher, self).__init__()
def emit(self, event: CodeEvent): def emit(self, event: BaseEvent):
self.message_all(event) self.message(event)
def emit_to(self, controller: str, event: CodeEvent): def emit_to(self, controller: str, event: BaseEvent):
self.message_to(controller, event) self.message_to(controller, event)

View File

@@ -1,5 +1,5 @@
""" """
DB Package Libs DB Package
""" """
from .models import User from .models import User

View File

@@ -1,5 +1,5 @@
""" """
DTO Class Package Libs DTO(s) Package
""" """
from .event import Event from .base_event import BaseEvent

View File

@@ -0,0 +1,14 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
@dataclass(slots = True)
class BaseEvent:
topic: str = None
content: any = None
raw_content: any = None

View File

@@ -1,9 +1,11 @@
""" """
Code DTO Class Package Libs Code DTO(s) Code Package
""" """
from .code_event import CodeEvent from .code_event import CodeEvent
from .register_provider_event import RegisterProviderEvent
from .get_command_system_event import GetCommandSystemEvent from .get_command_system_event import GetCommandSystemEvent
from .request_completion_event import RequestCompletionEvent from .request_completion_event import RequestCompletionEvent
from .cursor_moved_event import CursorMovedEvent from .cursor_moved_event import CursorMovedEvent
@@ -18,6 +20,7 @@ from .added_new_file_event import AddedNewFileEvent
from .swapped_file_event import SwappedFileEvent from .swapped_file_event import SwappedFileEvent
from .popped_file_event import PoppedFileEvent from .popped_file_event import PoppedFileEvent
from .removed_file_event import RemovedFileEvent from .removed_file_event import RemovedFileEvent
from .saved_file_event import SavedFileEvent
from .get_file_event import GetFileEvent from .get_file_event import GetFileEvent
from .get_swap_file_event import GetSwapFileEvent from .get_swap_file_event import GetSwapFileEvent

View File

@@ -4,11 +4,12 @@ from dataclasses import dataclass, field
# Lib imports # Lib imports
# Application imports # Application imports
from ..base_event import BaseEvent
@dataclass @dataclass
class CodeEvent: class CodeEvent(BaseEvent):
ignore_focus: bool = False ignore_focus: bool = False
view: any = None view: any = None
file: any = None file: any = None

View File

@@ -0,0 +1,19 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..base_event import BaseEvent
@dataclass
class RegisterProviderEvent(BaseEvent):
provider_name: str = ""
provider: GtkSource.CompletionProvider = None
language_ids: list = field(default_factory=lambda: [])

View File

@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
from .code_event import CodeEvent
@dataclass
class SavedFileEvent(CodeEvent):
...

View File

@@ -2,6 +2,11 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
# Lib imports # Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports # Application imports
from .code_event import CodeEvent from .code_event import CodeEvent
@@ -10,6 +15,6 @@ from .code_event import CodeEvent
@dataclass @dataclass
class TextInsertedEvent(CodeEvent): class TextInsertedEvent(CodeEvent):
line: int = 0 location: Gtk.TextIter = None
char: int = 0 text: str = ""
value: str = "" length: int = 0

View File

@@ -1,10 +0,0 @@
# Python imports
# Lib imports
# Application imports
class ObservableEvent:
...

View File

@@ -0,0 +1,3 @@
"""
Libs Plugin DTO(s) Package
"""

View File

@@ -6,9 +6,6 @@ from dataclasses import dataclass, field
# Application imports # Application imports
@dataclass @dataclass
class Event: class Requests:
topic: str bind_keys: list = field(default_factory = lambda: [])
content: str
raw_content: str

View File

@@ -0,0 +1,7 @@
"""
Code DTO States Package
"""
from .source_view_states import SourceViewStates
from .cursor_action import CursorAction
from .move_direction import MoveDirection

View File

@@ -0,0 +1,14 @@
# Python imports
from enum import Enum
# Lib imports
# Application imports
class CursorAction(Enum):
NONE = 0
DELETE = 1
BACKSPACE = 2
ENTER = 3

View File

@@ -0,0 +1,15 @@
# Python imports
from enum import Enum
# Lib imports
# Application imports
class MoveDirection(Enum):
NONE = 0
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4

View File

@@ -0,0 +1,14 @@
# Python imports
from enum import Enum
# Lib imports
# Application imports
class SourceViewStates(Enum):
INSERT = 0
MULTIINSERT = 1
COMMAND = 2
READONLY = 3

View File

@@ -1,22 +0,0 @@
# Python imports
# Lib imports
# Application imports
from .singleton import Singleton
class EndpointRegistry(Singleton):
def __init__(self):
self._endpoints = {}
def register(self, rule, **options):
def decorator(f):
self._endpoints[rule] = f
return f
return decorator
def get_endpoints(self):
return self._endpoints

Some files were not shown because too many files have changed in this diff Show More