From 6c42ff7c7d5d4e434de51a366465d15fe1ceec58 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Mon, 16 Feb 2026 17:11:30 -0600
Subject: [PATCH] Made words completer run async on load and update; swapped
out tabs to use Gtk.Notebook
---
.../provider_response_cache.py | 64 ++++++-------
.../provider_response_cache_base.py | 26 +++---
.../code/controllers/completion_controller.py | 8 --
.../code/controllers/tabs_controller.py | 49 +++++-----
.../views/source_views_controller.py | 6 +-
.../states/source_view_multi_insert_state.py | 4 +-
src/core/widgets/code/tab_widget.py | 48 ++++++----
src/core/widgets/code/tabs_widget.py | 90 ++++++++-----------
8 files changed, 132 insertions(+), 163 deletions(-)
diff --git a/plugins/completers/words_completer/provider_response_cache.py b/plugins/completers/words_completer/provider_response_cache.py
index 4194c28..00a20f6 100644
--- a/plugins/completers/words_completer/provider_response_cache.py
+++ b/plugins/completers/words_completer/provider_response_cache.py
@@ -1,5 +1,5 @@
# Python imports
-from os import path
+import asyncio
# Lib imports
import gi
@@ -23,10 +23,11 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
- self.load_as_new_set(event.file.buffer)
+ buffer = event.file.buffer
+ asyncio.run( self._handle_change(buffer) )
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
- self.matchers[event.file.buffer] = []
+ self.matchers[event.file.buffer] = set()
del self.matchers[event.file.buffer]
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
@@ -34,9 +35,23 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
buffer = event.file.buffer
- # if self.get_if_in_matched_word_set(buffer): return
- self.load_as_new_set(buffer)
+ asyncio.run( self._handle_change(buffer) )
+ async def _handle_change(self, buffer):
+ start_itr = buffer.get_start_iter()
+ end_itr = buffer.get_end_iter()
+ data = buffer.get_text(start_itr, end_itr, False)
+
+ if not data:
+ GLib.idle_add(self.load_empty_set, buffer)
+ return
+
+ if not buffer in self.matchers:
+ GLib.idle_add(self.load_as_new_set, buffer, data)
+ return
+
+ new_words = self.get_all_words(data)
+ GLib.idle_add(self.load_into_set, buffer, new_words)
def filter(self, word: str) -> list[dict]:
response: list[dict] = []
@@ -67,44 +82,15 @@ class ProviderResponseCache(ProviderResponseCacheBase):
return response
- def load_as_new_set(self, buffer):
- start_itr = buffer.get_start_iter()
- end_itr = buffer.get_end_iter()
- data = buffer.get_text(start_itr, end_itr, False)
+ def load_empty_set(self, buffer):
+ self.matchers[buffer] = set()
- if not data:
- self.matchers[buffer] = set()
- return
+ def load_into_set(self, buffer, new_words):
+ self.matchers[buffer].update(new_words)
+ def load_as_new_set(self, buffer, data):
self.matchers[buffer] = self.get_all_words(data)
- def get_if_in_matched_word_set(self, buffer):
- was_found = False
-
- if not buffer in self.matchers: return was_found
-
- insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
- end_itr = insert_itr.copy()
- start_itr = end_itr.copy()
-
- if not start_itr.starts_word():
- start_itr.backward_word_start()
-
- if not end_itr.ends_word():
- end_itr.forward_word_end()
-
- word = buffer.get_text(start_itr, end_itr, False)
- for _word in self.matchers[buffer]:
- if not _word.startswith(word): continue
- was_found = True
-
- if was_found: return was_found
-
- self.matchers[buffer].add(word)
-
- return was_found
-
-
def get_all_words(self, data: str):
words = set()
diff --git a/src/core/widgets/code/completion_providers/provider_response_cache_base.py b/src/core/widgets/code/completion_providers/provider_response_cache_base.py
index 568d29a..4dbbbad 100644
--- a/src/core/widgets/code/completion_providers/provider_response_cache_base.py
+++ b/src/core/widgets/code/completion_providers/provider_response_cache_base.py
@@ -38,19 +38,19 @@ class ProviderResponseCacheBase:
def process_file_change(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...")
- def filter(self, word: str):
+ def filter(self, word: str) -> list[dict]:
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter' not implemented...")
- def filter_with_context(self, context: GtkSource.CompletionContext):
+ def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
def create_completion_item(
self,
- label: str = "",
- text: str = "",
- info: str = "",
- completion: any = None
+ label: str = "",
+ text: str = "",
+ info: str = "",
+ icon: any = None
) -> dict:
if not label or not text: return
@@ -62,15 +62,15 @@ class ProviderResponseCacheBase:
comp_item.set_info(info)
# comp_item.set_markup(f"
{info}
")
- if completion:
+ if icon:
comp_item.set_icon(
- self.get_icon_for_type(completion.type)
+ self.get_icon_for_type(icon.type)
)
return comp_item
- def get_all_marks(self, buffer):
- marks = []
+ def get_all_marks(self, buffer) -> list:
+ marks: list = []
iter_ = buffer.get_start_iter()
while iter_:
@@ -85,8 +85,8 @@ class ProviderResponseCacheBase:
return marks
- def get_all_insert_marks(self, buffer):
- marks = []
+ def get_all_insert_marks(self, buffer) -> list:
+ marks: list = []
iter_ = buffer.get_start_iter()
while iter_:
@@ -115,5 +115,5 @@ class ProviderResponseCacheBase:
return buffer.get_text(start_iter, end_iter, False)
- def get_iter_correctly(self, context):
+ def get_iter_correctly(self, context) -> Gtk.TextIter:
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
diff --git a/src/core/widgets/code/controllers/completion_controller.py b/src/core/widgets/code/controllers/completion_controller.py
index f0205e1..647a18b 100644
--- a/src/core/widgets/code/controllers/completion_controller.py
+++ b/src/core/widgets/code/controllers/completion_controller.py
@@ -17,9 +17,6 @@ class CompletionController(ControllerBase):
def __init__(self):
super(CompletionController, self).__init__()
- self.words_provider = GtkSource.CompletionWords.new("words", None)
- self.words_provider.props.activation = GtkSource.CompletionActivation.INTERACTIVE
-
self._completers: list[GtkSource.Completion] = []
self._providers: dict[str, GtkSource.CompletionProvider] = {}
@@ -45,7 +42,6 @@ class CompletionController(ControllerBase):
def register_completer(self, completer: GtkSource.Completion):
self._completers.append(completer)
- # completer.add_provider(self.words_provider)
for provider in self._providers.values():
completer.add_provider(provider)
@@ -71,14 +67,10 @@ class CompletionController(ControllerBase):
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)
diff --git a/src/core/widgets/code/controllers/tabs_controller.py b/src/core/widgets/code/controllers/tabs_controller.py
index cd40ffe..cb3302e 100644
--- a/src/core/widgets/code/controllers/tabs_controller.py
+++ b/src/core/widgets/code/controllers/tabs_controller.py
@@ -1,6 +1,9 @@
# Python imports
# Lib imports
+import gi
+
+from gi.repository import Gtk
# Application imports
from libs.controllers.controller_base import ControllerBase
@@ -19,11 +22,15 @@ class TabsController(ControllerBase):
self.active_view: SourceView = None
self.tabs_widget: TabsWidget = TabsWidget()
+ self.tabs_widget.message = self.message
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.FocusedViewEvent):
self.active_view = event.view
+ self.tabs_widget.view_changed(
+ event.view.get_buffer()
+ )
elif isinstance(event, Code_Event_Types.FilePathSetEvent):
self.update_tab_label(event)
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
@@ -37,48 +44,32 @@ class TabsController(ControllerBase):
return self.tabs_widget
def update_tab_label(self, event: Code_Event_Types.FilePathSetEvent):
- for tab in self.tabs_widget.get_children():
+ for page_widget in self.tabs_widget.get_children():
+ tab = self.tabs_widget.get_tab_label(page_widget)
if not event.file == tab.file: continue
+
tab.label.set_label(event.file.fname)
+
break
def add_tab(self, event: Code_Event_Types.AddedNewFileEvent):
- def set_active_tab(tab, eve, file):
- event = Event_Factory.create_event(
- "set_active_file",
- buffer = tab.get_parent().file.buffer
- )
-
- self.active_view.set_buffer(
- tab.get_parent().file.buffer
- )
-
- self.message(event)
-
- def close_tab(tab, eve, file):
- event = Event_Factory.create_event(
- "remove_file",
- buffer = tab.get_parent().file.buffer
- )
-
- self.message(event)
-
+ box = Gtk.Separator()
tab = TabWidget()
+
tab.file = event.file
tab.label.set_label(event.file.fname)
- tab.set_select_signal(set_active_tab)
- tab.set_close_signal(close_tab)
- self.tabs_widget.add(tab)
- tab.show()
+ self.tabs_widget.append_page(box, tab)
+ tab.show_all()
def remove_tab(self, event: Code_Event_Types.RemovedFileEvent):
- for tab in self.tabs_widget.get_children():
+ for page_widget in self.tabs_widget.get_children():
+ tab = self.tabs_widget.get_tab_label(page_widget)
if not event.file == tab.file: continue
tab.clear_signals_and_data()
- tab.run_dispose()
- tab.destroy()
+ self.tabs_widget.remove_page(
+ self.tabs_widget.page_num(page_widget)
+ )
- del tab
break
diff --git a/src/core/widgets/code/controllers/views/source_views_controller.py b/src/core/widgets/code/controllers/views/source_views_controller.py
index 08c914e..f5c7e60 100644
--- a/src/core/widgets/code/controllers/views/source_views_controller.py
+++ b/src/core/widgets/code/controllers/views/source_views_controller.py
@@ -33,8 +33,10 @@ class SourceViewsController(ControllerBase, list):
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.SetActiveFileEvent):
+ self.signal_mapper.active_view.set_buffer(
+ event.buffer
+ )
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
self.signal_mapper.insert_text(event.file, event.text)
diff --git a/src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py b/src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py
index 689954f..3fab3e5 100644
--- a/src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py
+++ b/src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py
@@ -63,10 +63,10 @@ class SourceViewsMultiInsertState(MarkEventsMixin):
for mark in self.insert_markers:
end_itr = buffer.get_iter_at_mark(mark)
start_itr = end_itr.copy()
-
+
if not start_itr.starts_word():
start_itr.backward_word_start()
-
+
if not end_itr.ends_word():
end_itr.forward_word_end()
diff --git a/src/core/widgets/code/tab_widget.py b/src/core/widgets/code/tab_widget.py
index f1cc6c2..e677a61 100644
--- a/src/core/widgets/code/tab_widget.py
+++ b/src/core/widgets/code/tab_widget.py
@@ -10,52 +10,62 @@ from gi.repository import Gtk
class TabWidget(Gtk.Box):
+ """docstring for TabWidget"""
+
def __init__(self):
super(TabWidget, self).__init__()
- self.file = None
+ self._close_tab = None
self._setup_styling()
self._setup_signals()
- self._subscribe_to_events()
self._load_widgets()
- self.show_all()
-
def _setup_styling(self):
ctx = self.get_style_context()
ctx.add_class("tab-widget")
+ self.set_orientation(0)
+ self.set_hexpand(False)
+
def _setup_signals(self):
...
- def _subscribe_to_events(self):
- ...
-
def _load_widgets(self):
- self._label_eve_box = Gtk.EventBox()
- self.label = Gtk.Label(label = "")
- self.close_btn = Gtk.Button(label = "X")
+ self.label = Gtk.Label()
+ self.close_btn = Gtk.Button()
+ icon = Gtk.Image(stock = Gtk.STOCK_CLOSE)
ctx = self.label.get_style_context()
ctx.add_class("tab-label")
ctx = self.close_btn.get_style_context()
ctx.add_class("tab-close-bttn")
+ self.label.set_xalign(0.0)
+ self.label.set_margin_left(25)
+ self.label.set_margin_right(25)
self.label.set_hexpand(True)
- self._label_eve_box.add(self.label)
- self.add(self._label_eve_box)
+ self.close_btn.add(icon)
+ self.add(self.label)
self.add(self.close_btn)
- def clear_signals_and_data(self):
- del self.file
- self._label_eve_box.disconnect(self._label_eve_box_id)
- self.close_btn.disconnect(self.close_btn_id)
+ self.show_all()
- def set_select_signal(self, callback):
- self._label_eve_box_id = self._label_eve_box.connect('button-release-event', callback, self.file)
+ def clear_signals_and_data(self):
+ self.close_btn.disconnect(self._handler_id)
+ self._close_tab = None
+ self._handler_id = None
+
+ for child in self.get_children():
+ child.unparent()
+ child.run_dispose()
+ child.destroy()
def set_close_signal(self, callback):
- self.close_btn_id = self.close_btn.connect('button-release-event', callback, self.file)
+ self._handler_id = self.close_btn.connect(
+ 'button-release-event',
+ callback,
+ self.file
+ )
diff --git a/src/core/widgets/code/tabs_widget.py b/src/core/widgets/code/tabs_widget.py
index 88840ca..bd16986 100644
--- a/src/core/widgets/code/tabs_widget.py
+++ b/src/core/widgets/code/tabs_widget.py
@@ -6,16 +6,13 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
-from libs.event_factory import Code_Event_Types
-
-from .source_view import SourceView
-from .source_file import SourceFile
+from libs.event_factory import Event_Factory, Code_Event_Types
from .tab_widget import TabWidget
-class TabsWidget(Gtk.ButtonBox):
+class TabsWidget(Gtk.Notebook):
def __init__(self):
super(TabsWidget, self).__init__()
@@ -26,10 +23,12 @@ class TabsWidget(Gtk.ButtonBox):
def _setup_styling(self):
- self.set_layout(Gtk.ButtonBoxStyle.CENTER)
+ ...
def _setup_signals(self):
- ...
+ self.connect("page-added", self._page_added)
+ self.switch_page_id = \
+ self.connect_after("switch-page", self._switch_page)
def _subscribe_to_events(self):
...
@@ -37,51 +36,40 @@ class TabsWidget(Gtk.ButtonBox):
def _load_widgets(self):
...
- def add_tab(self, event: Code_Event_Types.CodeEvent):
- """Add a tab widget for the given file event."""
- if not hasattr(self, 'tabs'):
- return
-
- tab = TabWidget()
- tab.file = event.file
+ def _page_added(self, notebook, page_widget, page_num):
+ tab = self.get_tab_label(page_widget)
+ tab.set_close_signal(self._close_tab)
- tab.label.set_label(event.file.fname)
+ page_widget.show()
+ self.set_tab_detachable(page_widget, True)
+ self.set_tab_reorderable(page_widget, True)
- def select_signal(widget, eve, file):
- self.code_base.active_view.command.exec_with_args(
- "set_buffer",
- (self.code_base.active_view, file)
+ def _close_tab(self, tab, eve, file):
+ event = Event_Factory.create_event(
+ "remove_file",
+ buffer = tab.get_parent().file.buffer
+ )
+
+ self.message(event)
+
+ def _switch_page(self, notebook, page_widget, page_num):
+ tab = self.get_tab_label(page_widget)
+ event = Event_Factory.create_event(
+ "set_active_file",
+ buffer = tab.file.buffer
+ )
+
+ self.message(event)
+
+ def view_changed(self, buffer):
+ for page_widget in self.get_children():
+ tab = self.get_tab_label(page_widget)
+ if not buffer == tab.file.buffer: continue
+
+ self.handler_block(self.switch_page_id)
+
+ self.set_current_page(
+ self.page_num(page_widget)
)
- def close_signal(widget, eve, file):
- self.code_base.files_controller.remove_file(file.buffer)
-
- tab.set_select_signal(select_signal)
- tab.set_close_signal(close_signal)
-
- self.tabs.add(tab)
-
- def remove_tab(self, event: Code_Event_Types.CodeEvent):
- """Remove a tab widget for the given file event."""
- if not hasattr(self, 'tabs'):
- return
-
- for child in self.tabs.get_children():
- if not child.file == event.file: continue
-
- self.tabs.remove(child)
- child.clear_signals_and_data()
- del child
-
- return
-
- def update_tab_label(self, event: Code_Event_Types.CodeEvent):
- """Update tab label for the given file event."""
- if not hasattr(self, 'tabs'):
- return
-
- for tab in self.tabs.get_children():
- if not tab.file == event.file: continue
- tab.label.set_label(event.file.fname)
-
- return
+ self.handler_unblock(self.switch_page_id)