From 7a343e39e8795e766671c63769cc5595eff750ae Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sat, 21 Oct 2023 15:46:17 -0500 Subject: [PATCH] Restructuring buffer access to provide better separation for future work --- .../widgets/base/sourceview/auto_indenter.py | 23 ++++++ .../python_completion_provider.py | 2 +- .../sourceview/source_file_events_mixin.py | 23 +++--- .../sourceview/source_marks_events_mixin.py | 48 ++++++----- .../widgets/base/sourceview/source_view.py | 79 +++++++++++-------- .../base/sourceview/source_view_events.py | 77 ++++++++++-------- src/core/widgets/controls/theme_button.py | 2 +- src/core/widgets/miniview_widget.py | 2 +- 8 files changed, 153 insertions(+), 103 deletions(-) create mode 100644 src/core/widgets/base/sourceview/auto_indenter.py diff --git a/src/core/widgets/base/sourceview/auto_indenter.py b/src/core/widgets/base/sourceview/auto_indenter.py new file mode 100644 index 0000000..ead9ac7 --- /dev/null +++ b/src/core/widgets/base/sourceview/auto_indenter.py @@ -0,0 +1,23 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('GtkSource', '4') +from gi.repository import GtkSource + +# Application imports + + + +# NOTE: GtkSource 5 allows for smart indent action by allowing us to override the default auto indent logic... +# In the long run this will be better because we can check not only for :, ;, { or other things but apply per language such as bash where +# there isn't a special char but words... +# class AutoIndenter(GtkSource.Indenter): +# def __init__(self): +# ... +# +# def indent(self, view, iter): +# ... +# +# def is_trigger(self, view, iter, modifier, keyval): +# print(iter.get_char()) diff --git a/src/core/widgets/base/sourceview/custom_completion_providers/python_completion_provider.py b/src/core/widgets/base/sourceview/custom_completion_providers/python_completion_provider.py index 1965cfe..d8268fe 100644 --- a/src/core/widgets/base/sourceview/custom_completion_providers/python_completion_provider.py +++ b/src/core/widgets/base/sourceview/custom_completion_providers/python_completion_provider.py @@ -105,4 +105,4 @@ class PythonCompletionProvider(GObject.Object, GtkSource.CompletionProvider): try: return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0) except: - return None + return None \ No newline at end of file diff --git a/src/core/widgets/base/sourceview/source_file_events_mixin.py b/src/core/widgets/base/sourceview/source_file_events_mixin.py index 74e72b9..e0c4881 100644 --- a/src/core/widgets/base/sourceview/source_file_events_mixin.py +++ b/src/core/widgets/base/sourceview/source_file_events_mixin.py @@ -64,14 +64,14 @@ class FileEventsMixin: self.update_labels(gfile) return - file = GtkSource.File() + file = GtkSource.File() + buffer = self.get_buffer() file.set_location(gfile) - self._file_loader = GtkSource.FileLoader.new(self._buffer, file) + self._file_loader = GtkSource.FileLoader.new(buffer, file) - def finish_load_callback(obj, res, user_data=None): + def finish_load_callback(obj, res, user_data = None): self._file_loader.load_finish(res) - self._document_loaded() - self.got_to_line(line) + self._document_loaded(line) self.update_labels(gfile) self._loading_file = False @@ -97,7 +97,8 @@ class FileEventsMixin: Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]: - self._buffer.set_modified(True) + buffer = self.get_buffer() + buffer.set_modified(True) if eve_type in [ Gio.FileMonitorEvent.CHANGES_DONE_HINT ]: if self._ignore_internal_change: @@ -116,16 +117,16 @@ class FileEventsMixin: def _write_file(self, gfile, save_as = False): if not gfile: return + buffer = self.get_buffer() with open(gfile.get_path(), 'w') as f: if not save_as: self._ignore_internal_change = True - start_itr = self._buffer.get_start_iter() - end_itr = self._buffer.get_end_iter() - text = self._buffer.get_text(start_itr, end_itr, True) + start_itr = buffer.get_start_iter() + end_itr = buffer.get_end_iter() + text = buffer.get_text(start_itr, end_itr, True) f.write(text) - f.close() - self._buffer.set_modified(False) + buffer.set_modified(False) return gfile diff --git a/src/core/widgets/base/sourceview/source_marks_events_mixin.py b/src/core/widgets/base/sourceview/source_marks_events_mixin.py index d33c658..2c8bf92 100644 --- a/src/core/widgets/base/sourceview/source_marks_events_mixin.py +++ b/src/core/widgets/base/sourceview/source_marks_events_mixin.py @@ -13,8 +13,10 @@ from gi.repository import Gtk class MarkEventsMixin: def keyboard_insert_mark(self, target_iter = None, is_keyboard_insert = True): + buffer = self.get_buffer() + if not target_iter: - target_iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() ) + target_iter = buffer.get_iter_at_mark( buffer.get_insert() ) found_mark = self.check_for_insert_marks(target_iter, is_keyboard_insert) if not found_mark: @@ -22,7 +24,7 @@ class MarkEventsMixin: hash = "%032x" % random_bits mark = Gtk.TextMark.new(name = f"multi_insert_{hash}", left_gravity = False) - self._buffer.add_mark(mark, target_iter) + buffer.add_mark(mark, target_iter) self._multi_insert_marks.append(mark) mark.set_visible(True) @@ -38,13 +40,15 @@ class MarkEventsMixin: self.keyboard_insert_mark(target_iter, is_keyboard_insert = False) def check_for_insert_marks(self, target_iter, is_keyboard_insert): - marks = target_iter.get_marks() + marks = target_iter.get_marks() + buffer = self.get_buffer() found_mark = False + for mark in marks: for _mark in self._multi_insert_marks: if _mark == mark: mark.set_visible(False) - self._buffer.delete_mark(mark) + buffer.delete_mark(mark) found_mark = True break @@ -60,39 +64,41 @@ class MarkEventsMixin: return found_mark def keyboard_clear_marks(self): - self._buffer.begin_user_action() + buffer = self.get_buffer() + + buffer.begin_user_action() for mark in self._multi_insert_marks: mark.set_visible(False) - self._buffer.delete_mark(mark) + buffer.delete_mark(mark) self._multi_insert_marks.clear() - self._buffer.end_user_action() + buffer.end_user_action() - def _update_multi_line_markers(self, text_str): + def _update_multi_line_markers(self, buffer, text_str): for mark in self._multi_insert_marks: - iter = self._buffer.get_iter_at_mark(mark) - self._buffer.insert(iter, text_str, -1) + iter = buffer.get_iter_at_mark(mark) + buffer.insert(iter, text_str, -1) - self.end_user_action() + self.end_user_action(buffer) - def _delete_on_multi_line_markers(self): - iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() ) - self._buffer.backspace(iter, interactive = True, default_editable = True) + def _delete_on_multi_line_markers(self, buffer): + iter = buffer.get_iter_at_mark( buffer.get_insert() ) + buffer.backspace(iter, interactive = True, default_editable = True) for mark in self._multi_insert_marks: - iter = self._buffer.get_iter_at_mark(mark) - self._buffer.backspace(iter, interactive = True, default_editable = True) + iter = buffer.get_iter_at_mark(mark) + buffer.backspace(iter, interactive = True, default_editable = True) - self.end_user_action() + self.end_user_action(buffer) - def begin_user_action(self): + def begin_user_action(self, buffer): if len(self._multi_insert_marks) > 0: - self._buffer.begin_user_action() + buffer.begin_user_action() self.freeze_multi_line_insert = True - def end_user_action(self): + def end_user_action(self, buffer): if len(self._multi_insert_marks) > 0: - self._buffer.end_user_action() + buffer.end_user_action() self.freeze_multi_line_insert = False diff --git a/src/core/widgets/base/sourceview/source_view.py b/src/core/widgets/base/sourceview/source_view.py index a3bbaa1..75236ee 100644 --- a/src/core/widgets/base/sourceview/source_view.py +++ b/src/core/widgets/base/sourceview/source_view.py @@ -12,6 +12,7 @@ from gi.repository import Gio from gi.repository import GtkSource # Application imports +# from .auto_indenter import AutoIndenter from .source_view_events import SourceViewEventsMixin from .custom_completion_providers.example_completion_provider import ExampleCompletionProvider from .custom_completion_providers.python_completion_provider import PythonCompletionProvider @@ -36,7 +37,6 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): self._skip_file_load = False self._ignore_internal_change = False self._loading_file = False - self._buffer = self.get_buffer() self._completion = self.get_completion() self._px_value = settings.theming.default_zoom @@ -55,6 +55,7 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): ctx.add_class("source-view") ctx.add_class(f"px{self._px_value}") + self.set_vexpand(True) self.set_show_line_marks(True) self.set_show_line_numbers(True) @@ -67,12 +68,13 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): self.set_show_right_margin(True) self.set_right_margin_position(80) self.set_background_pattern(0) # 0 = None, 1 = Grid + # NOTE: Add back once we move to Gtk 4 and use GtkSource 5 + # self.set_indenter( AutoIndenter() ) - self._create_default_tag() - self.set_buffer_language() - self.set_buffer_style() - - self.set_vexpand(True) + buffer = self.get_buffer() + self._create_default_tag(buffer) + self.set_buffer_language(buffer) + self.set_buffer_style(buffer) def _setup_signals(self): @@ -84,10 +86,12 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): self.connect("button-press-event", self._button_press_event) self.connect("scroll-event", self._scroll_event) - self._buffer.connect('changed', self._is_modified) - self._buffer.connect("mark-set", self._on_cursor_move) - self._buffer.connect('insert-text', self._insert_text) - self._buffer.connect('modified-changed', self._buffer_modified_changed) + buffer = self.get_buffer() + buffer.connect('changed', self._is_modified) + buffer.connect("mark-set", self._on_cursor_move) + buffer.connect('insert-text', self._insert_text) + buffer.connect('modified-changed', self._buffer_modified_changed) + def _subscribe_to_events(self): ... @@ -96,14 +100,15 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): ... - def _document_loaded(self): + def _document_loaded(self, line: int = 0): for provider in self._completion.get_providers(): self._completion.remove_provider(provider) # TODO: actually load a meaningful provider based on file type... - file = self._current_file.get_path() + file = self._current_file.get_path() + buffer = self.get_buffer() word_completion = GtkSource.CompletionWords.new("word_completion") - word_completion.register(self._buffer) + word_completion.register(buffer) self._completion.add_provider(word_completion) # example_completion_provider = ExampleCompletionProvider() @@ -111,27 +116,30 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): # py_completion_provider = PythonCompletionProvider(file) # self._completion.add_provider(py_completion_provider) + self.got_to_line(buffer, line) - def _create_default_tag(self): - general_style_tag = self._buffer.create_tag('general_style') + def _create_default_tag(self, buffer): + general_style_tag = buffer.create_tag('general_style') general_style_tag.set_property('size', 100) general_style_tag.set_property('scale', 100) def _is_modified(self, *args): + buffer = self.get_buffer() + if not self._loading_file: - event_system.emit("buffer_changed", (self._buffer, )) + event_system.emit("buffer_changed", (buffer, )) else: - event_system.emit("buffer_changed_first_load", (self._buffer, )) + event_system.emit("buffer_changed_first_load", (buffer, )) - self.update_cursor_position() + self.update_cursor_position(buffer) - def _insert_text(self, text_buffer, location_itr, text_str, len_int): + def _insert_text(self, buffer, location_itr, text_str, len_int): if self.freeze_multi_line_insert: return - self.begin_user_action() - with self._buffer.freeze_notify(): - GLib.idle_add(self._update_multi_line_markers, *(text_str,)) + self.begin_user_action(buffer) + with buffer.freeze_notify(): + GLib.idle_add(self._update_multi_line_markers, *(buffer, text_str,)) def _buffer_modified_changed(self, buffer): tab_widget = self.get_parent().get_tab_widget() @@ -144,6 +152,7 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK) is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False + buffer = self.get_buffer() try: is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False @@ -156,7 +165,7 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): if is_shift: if keyname in [ "z", "Up", "Down", "Left", "Right" ]: - # NOTE: For now do like so for completion sake above. + # NOTE: For now do like so for completion sake above. if keyname in ["Left", "Right"]: return False @@ -168,9 +177,9 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): if keyname == "BackSpace": if len(self._multi_insert_marks) > 0: - self.begin_user_action() - with self._buffer.freeze_notify(): - GLib.idle_add(self._delete_on_multi_line_markers) + self.begin_user_action(buffer) + with buffer.freeze_notify(): + GLib.idle_add(self._delete_on_multi_line_markers, *(buffer,)) return True @@ -190,12 +199,13 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): def _scroll_event(self, widget, eve): accel_mask = Gtk.accelerator_get_default_mod_mask() - x, y, z = eve.get_scroll_deltas() + x, y, z = eve.get_scroll_deltas() if eve.state & accel_mask == Gdk.ModifierType.CONTROL_MASK: + buffer = self.get_buffer() if z > 0: - self.scale_down_text() + self.scale_down_text(buffer) else: - self.scale_up_text() + self.scale_up_text(buffer) return True @@ -226,10 +236,10 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): return False - def _on_cursor_move(self, buf, cursor_iter, mark, user_data = None): - if mark != buf.get_insert(): return + def _on_cursor_move(self, buffer, cursor_iter, mark, user_data = None): + if mark != buffer.get_insert(): return - self.update_cursor_position() + self.update_cursor_position(buffer) # NOTE: Not sure but this might not be efficient if the map reloads the same view... event_system.emit(f"set_source_view", (self,)) @@ -246,12 +256,13 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): if info == 70: return if info == 80: - uris = data.get_uris() + buffer = self.get_buffer() + uris = data.get_uris() if len(uris) == 0: uris = data.get_text().split("\n") - if not self._current_file and not self._buffer.get_modified(): + if not self._current_file and not buffer.get_modified(): gfile = Gio.File.new_for_uri(uris[0]) self.open_file(gfile) uris.pop(0) diff --git a/src/core/widgets/base/sourceview/source_view_events.py b/src/core/widgets/base/sourceview/source_view_events.py index 865cf42..ed4f317 100644 --- a/src/core/widgets/base/sourceview/source_view_events.py +++ b/src/core/widgets/base/sourceview/source_view_events.py @@ -13,16 +13,16 @@ from .source_marks_events_mixin import MarkEventsMixin class SourceViewEventsMixin(MarkEventsMixin, FileEventsMixin): - def set_buffer_language(self, language = "python3"): - self._buffer.set_language( self._language_manager.get_language(language) ) + def set_buffer_language(self, buffer, language = "python3"): + buffer.set_language( self._language_manager.get_language(language) ) - def set_buffer_style(self, style = settings.theming.syntax_theme): - self._buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) ) + def set_buffer_style(self, buffer, style = settings.theming.syntax_theme): + buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) ) def toggle_highlight_line(self, widget = None, eve = None): self.set_highlight_current_line( not self.get_highlight_current_line() ) - def scale_up_text(self, scale_step = 10): + def scale_up_text(self, buffer, scale_step = 10): ctx = self.get_style_context() if self._px_value < 99: @@ -30,15 +30,15 @@ class SourceViewEventsMixin(MarkEventsMixin, FileEventsMixin): ctx.add_class(f"px{self._px_value}") # NOTE: Hope to bring this or similar back after we decouple scaling issues coupled with the miniview. - # tag_table = self._buffer.get_tag_table() - # start_itr = self._buffer.get_start_iter() - # end_itr = self._buffer.get_end_iter() + # tag_table = buffer.get_tag_table() + # start_itr = buffer.get_start_iter() + # end_itr = buffer.get_end_iter() # tag = tag_table.lookup('general_style') # # tag.set_property('scale', tag.get_property('scale') + scale_step) - # self._buffer.apply_tag(tag, start_itr, end_itr) + # buffer.apply_tag(tag, start_itr, end_itr) - def scale_down_text(self, scale_step = 10): + def scale_down_text(self, buffer, scale_step = 10): ctx = self.get_style_context() if self._px_value > 1: @@ -47,52 +47,61 @@ class SourceViewEventsMixin(MarkEventsMixin, FileEventsMixin): ctx.add_class(f"px{self._px_value}") # NOTE: Hope to bring this or similar back after we decouple scaling issues coupled with the miniview. - # tag_table = self._buffer.get_tag_table() - # start_itr = self._buffer.get_start_iter() - # end_itr = self._buffer.get_end_iter() + # tag_table = buffer.get_tag_table() + # start_itr = buffer.get_start_iter() + # end_itr = buffer.get_end_iter() # tag = tag_table.lookup('general_style') # # tag.set_property('scale', tag.get_property('scale') - scale_step) - # self._buffer.apply_tag(tag, start_itr, end_itr) + # buffer.apply_tag(tag, start_itr, end_itr) - def update_cursor_position(self): - iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() ) - chars = iter.get_offset() - row = iter.get_line() + 1 - col = self.get_visual_column(iter) + 1 + def update_cursor_position(self, buffer = None): + buffer = self.get_buffer() if not buffer else buffer + iter = buffer.get_iter_at_mark( buffer.get_insert() ) + chars = iter.get_offset() + row = iter.get_line() + 1 + col = self.get_visual_column(iter) + 1 event_system.emit("set_line_char_label", (f"{row}:{col}",)) - def got_to_line(self, line: int = 0): - index = line - buffer = self.get_buffer() - line_itr = buffer.get_iter_at_line(index) - char_iter = buffer.get_iter_at_line_offset(index, line_itr.get_bytes_in_line()) + def got_to_line(self, buffer = None, line: int = 0): + buffer = self.get_buffer() if not buffer else buffer + line_itr = buffer.get_iter_at_line(line) + char_iter = buffer.get_iter_at_line_offset(line, line_itr.get_bytes_in_line()) buffer.place_cursor(char_iter) if not buffer.get_mark("starting_cursor"): buffer.create_mark("starting_cursor", char_iter, True) self.scroll_to_mark( buffer.get_mark("starting_cursor"), 0.0, True, 0.0, 0.0 ) - - # https://github.com/ptomato/inform7-ide/blob/main/src/actions.c - def action_uncomment_selection(self): - ... - - def action_comment_out_selection(self): - ... - def keyboard_undo(self): - self._buffer.undo() + buffer = self.get_buffer() + buffer.undo() def keyboard_redo(self): - self._buffer.redo() + buffer = self.get_buffer() + buffer.redo() def keyboard_move_lines_up(self): + buffer = self.get_buffer() + + self.begin_user_action(buffer) + self.emit("move-lines", *(False,)) + # unindent_lines + # self.emit("move-words", *(self, 4,)) + + self.end_user_action(buffer) def keyboard_move_lines_down(self): + buffer = self.get_buffer() + + self.begin_user_action(buffer) + self.emit("move-lines", *(True,)) + # self.emit("move-words", *(self, -4,)) + + self.end_user_action(buffer) def update_labels(self, gfile = None): if not gfile: return diff --git a/src/core/widgets/controls/theme_button.py b/src/core/widgets/controls/theme_button.py index 2add82a..5fe2698 100644 --- a/src/core/widgets/controls/theme_button.py +++ b/src/core/widgets/controls/theme_button.py @@ -82,4 +82,4 @@ class ThemeButton(Gtk.Button): def _show_popover(self, widget, eve = None): - event_system.emit("show_theme_popup") + event_system.emit("show_theme_popup") \ No newline at end of file diff --git a/src/core/widgets/miniview_widget.py b/src/core/widgets/miniview_widget.py index 79b58af..a0e1dca 100644 --- a/src/core/widgets/miniview_widget.py +++ b/src/core/widgets/miniview_widget.py @@ -37,4 +37,4 @@ class MiniViewWidget(Map): ... def set_source_view(self, source_view): - self.set_view(source_view) + self.set_view(source_view) \ No newline at end of file