From 3d37a2335ac6ab479b3e02381869d72844677751 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Wed, 25 Sep 2024 02:11:39 -0500 Subject: [PATCH] Implementing own completion UI logic --- .../base/notebook/editor_controller.py | 54 ++++++++++++++--- .../lsp_completion_provider.py | 3 + .../base/sourceview/key_input_controller.py | 20 +++++++ .../mixins/source_file_events_mixin.py | 10 ++-- .../widgets/base/sourceview/source_view.py | 7 ++- src/core/widgets/completion_item.py | 44 ++++++++++++++ src/core/widgets/completion_view.py | 59 +++++++++++++++++++ 7 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 src/core/widgets/completion_item.py create mode 100644 src/core/widgets/completion_view.py diff --git a/src/core/widgets/base/notebook/editor_controller.py b/src/core/widgets/base/notebook/editor_controller.py index 55743a7..77a3285 100644 --- a/src/core/widgets/base/notebook/editor_controller.py +++ b/src/core/widgets/base/notebook/editor_controller.py @@ -11,6 +11,7 @@ from gi.repository import GtkSource from libs.dto.lsp_message_structs import LSPResponseTypes, LSPResponseRequest, LSPResponseNotification from .key_input_controller import KeyInputController from .editor_events import EditorEventsMixin +from ...completion_item import CompletionItem @@ -44,7 +45,7 @@ class EditorControllerMixin(KeyInputController, EditorEventsMixin): page_num = None container = None - logger.debug( repr(message) ) + logger.debug( f"\n\n{repr(message)}\n\n" ) if isinstance(message, dict): ... @@ -53,14 +54,30 @@ class EditorControllerMixin(KeyInputController, EditorEventsMixin): if type(message.result) is dict: keys = message.result.keys() - if "items" in keys: # completion - completion = source_view.get_completion() - providers = completion.get_providers() + if "items" in keys: # completion + if source_view.completion_view.get_parent(): + source_view.remove(source_view.completion_view) + + source_view.completion_view.clear_items() + x, y = self._get_insert_line_xy(source_view) + source_view.add_child_in_window(source_view.completion_view, Gtk.TextWindowType.WIDGET, x, y) + + for item in message.result["items"]: + ci = CompletionItem() + ci.populate_completion_item(item) + source_view.completion_view.add_completion_item(ci) + + source_view.completion_view.show_all() + + + # completion = source_view.get_completion() + # providers = completion.get_providers() + + # for provider in providers: + # if provider.__class__.__name__ == 'LSPCompletionProvider': + # source_view.completion_items = message.result["items"] + # source_view.emit("show-completion") - for provider in providers: - if provider.__class__.__name__ == 'LSPCompletionProvider': - source_view.completion_items = message.result["items"] - source_view.emit("show-completion") if "result" in keys: ... @@ -77,4 +94,23 @@ class EditorControllerMixin(KeyInputController, EditorEventsMixin): if message.method == "textDocument/publshDiagnostics": ... - source_view = None \ No newline at end of file + source_view = None + + + + # Gotten logic from: + # https://stackoverflow.com/questions/7139645/find-the-cursor-position-on-a-gtksourceview-window + def _get_insert_line_xy(self, source_view): + buffer = source_view.get_buffer() + iter = buffer.get_iter_at_mark( buffer.get_insert() ) + iter_loc = source_view.get_iter_location(iter) + + win_loc = source_view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, iter_loc.x, iter_loc.y) + + win = source_view.get_window( Gtk.TextWindowType.WIDGET ) + view_pos = win.get_position() + + xx = win_loc[0] + view_pos[0] + yy = win_loc[1] + view_pos[1] + iter_loc.height + + return xx, yy \ No newline at end of file diff --git a/src/core/widgets/base/sourceview/custom_completion_providers/lsp_completion_provider.py b/src/core/widgets/base/sourceview/custom_completion_providers/lsp_completion_provider.py index 75bfea0..b3d02bc 100644 --- a/src/core/widgets/base/sourceview/custom_completion_providers/lsp_completion_provider.py +++ b/src/core/widgets/base/sourceview/custom_completion_providers/lsp_completion_provider.py @@ -73,6 +73,9 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider): if "insertText" in keys: comp_item.set_text(item["insertText"]) + if "additionalTextEdits" in keys: + comp_item.additionalTextEdits = item["additionalTextEdits"] + return comp_item diff --git a/src/core/widgets/base/sourceview/key_input_controller.py b/src/core/widgets/base/sourceview/key_input_controller.py index af2f1c8..53233fe 100644 --- a/src/core/widgets/base/sourceview/key_input_controller.py +++ b/src/core/widgets/base/sourceview/key_input_controller.py @@ -47,6 +47,10 @@ class KeyInputController: if keyname in [ "Up", "Down", "Left", "Right" ]: return True + if keyname in [ "Return", "Enter", "Up", "Down" ]: + if self.completion_view.get_parent() and self.completion_view.is_visible(): + return True + if len(self._multi_insert_marks) > 0: if keyname == "BackSpace": @@ -125,6 +129,22 @@ class KeyInputController: self.keyboard_clear_marks() + if keyname in [ "Return", "Enter", "Up", "Down", "Left", "Right" ]: + if self.completion_view.get_parent() and self.completion_view.is_visible(): + if keyname in {"Return", "Enter"}: + self.completion_view.activate_completion() + if keyname == "UP": + self.completion_view.move_selection_up() + if keyname == "Down": + self.completion_view.move_selection_down() + if keyname == "Left": + self.remove( self.completion_view ) + if keyname == "Right": + self.remove( self.completion_view ) + + return True + + if keyname in {"Return", "Enter"}: if len(self._multi_insert_marks) > 0: self.begin_user_action(buffer) diff --git a/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py b/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py index 643b3b6..55374d9 100644 --- a/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py +++ b/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py @@ -149,12 +149,12 @@ class FileEventsMixin: event_system.emit("textDocument/didOpen", (self._current_filetype, uri, self.get_text())) - word_completion = GtkSource.CompletionWords.new("word_completion") - word_completion.register(buffer) - self._completion.add_provider(word_completion) + # word_completion = GtkSource.CompletionWords.new("word_completion") + # word_completion.register(buffer) + # self._completion.add_provider(word_completion) - lsp_completion_provider = LSPCompletionProvider(self) - self._completion.add_provider(lsp_completion_provider) + # lsp_completion_provider = LSPCompletionProvider(self) + # self._completion.add_provider(lsp_completion_provider) self.got_to_line(buffer, line) event_system.emit("buffer_changed_first_load", (buffer, )) \ No newline at end of file diff --git a/src/core/widgets/base/sourceview/source_view.py b/src/core/widgets/base/sourceview/source_view.py index df8eed1..520a41d 100644 --- a/src/core/widgets/base/sourceview/source_view.py +++ b/src/core/widgets/base/sourceview/source_view.py @@ -17,6 +17,7 @@ from .source_view_controller import SourceViewControllerMixin # from .custom_completion_providers.example_completion_provider import ExampleCompletionProvider # from .custom_completion_providers.python_completion_provider import PythonCompletionProvider +from ...completion_view import CompletionView @@ -46,9 +47,11 @@ class SourceView(SourceViewControllerMixin, GtkSource.View): self._px_value = settings.theming.default_zoom self._multi_insert_marks = [] - self.completion_items = [] self.freeze_multi_line_insert = False + self.completion_view = CompletionView() + # self.completion_items = [] + self._setup_styling() self._setup_signals() self._subscribe_to_events() @@ -117,4 +120,4 @@ class SourceView(SourceViewControllerMixin, GtkSource.View): self._timer.start() def _clear_cut_buffer(self): - self._cut_buffer = "" \ No newline at end of file + self._cut_buffer = "" diff --git a/src/core/widgets/completion_item.py b/src/core/widgets/completion_item.py new file mode 100644 index 0000000..150667d --- /dev/null +++ b/src/core/widgets/completion_item.py @@ -0,0 +1,44 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports + + + +class CompletionItem(Gtk.Button): + def __init__(self): + super(CompletionItem, self).__init__() + + self.insertText: str = "" + self.additionalTextEdits: [] = [] + + self._setup_styling() + self._setup_signals() + + self.show() + + + def _setup_styling(self): + ctx = self.get_style_context() + ctx.add_class("completion-item") + + def _setup_signals(self): + self.connect("clicked", self._do_completion) + + + def populate_completion_item(self, item): + keys = item.keys() + self.set_label(item["label"]) + + if "insertText" in keys: + self.insertText = item["insertText"] + + if "additionalTextEdits" in keys: + self.additionalTextEdits = item["additionalTextEdits"] + + def _do_completion(self, button): + ... \ No newline at end of file diff --git a/src/core/widgets/completion_view.py b/src/core/widgets/completion_view.py new file mode 100644 index 0000000..98fe9b5 --- /dev/null +++ b/src/core/widgets/completion_view.py @@ -0,0 +1,59 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports +from .completion_item import CompletionItem + + + +class CompletionView(Gtk.ScrolledWindow): + def __init__(self): + super(CompletionView, self).__init__() + + self.button_box = None + + self._setup_styling() + self._setup_signals() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + ctx = self.get_style_context() + ctx.add_class("completion-view") + self.set_size_request(320, 320) + + def _setup_signals(self): + ... + + def _load_widgets(self): + viewport = Gtk.Viewport() + self.button_box = Gtk.Box() + + self.button_box.set_orientation( Gtk.Orientation.VERTICAL ) + self.button_box.set_hexpand( True ) + + viewport.add(self.button_box) + self.add(viewport) + + def add_completion_item(self, item: CompletionItem): + self.button_box.add(item) + + def clear_items(self): + for child in self.button_box.get_children(): + self.button_box.remove(child) + + def activate_completion(self): + ... + + def move_selection_up(self): + ... + + def move_selection_down(self): + ... +