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)