From 7aca4d6960d24ca247a66820918870bceab63dff Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sun, 15 Oct 2023 22:36:33 -0500 Subject: [PATCH] Added autopairs plugin --- plugins/autopairs/__init__.py | 3 + plugins/autopairs/__main__.py | 3 + plugins/autopairs/manifest.json | 12 ++ plugins/autopairs/plugin.py | 165 ++++++++++++++++++ .../widgets/base/sourceview/source_view.py | 3 + 5 files changed, 186 insertions(+) create mode 100644 plugins/autopairs/__init__.py create mode 100644 plugins/autopairs/__main__.py create mode 100644 plugins/autopairs/manifest.json create mode 100644 plugins/autopairs/plugin.py diff --git a/plugins/autopairs/__init__.py b/plugins/autopairs/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/autopairs/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/autopairs/__main__.py b/plugins/autopairs/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/autopairs/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/autopairs/manifest.json b/plugins/autopairs/manifest.json new file mode 100644 index 0000000..9f4c039 --- /dev/null +++ b/plugins/autopairs/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest": { + "name": "Autopairs", + "author": "ITDominator", + "credit": "Hamad Al Marri", + "version": "0.0.1", + "support": "", + "requests": { + "pass_events": "true" + } + } +} \ No newline at end of file diff --git a/plugins/autopairs/plugin.py b/plugins/autopairs/plugin.py new file mode 100644 index 0000000..364547a --- /dev/null +++ b/plugins/autopairs/plugin.py @@ -0,0 +1,165 @@ +# Python imports +import os +import threading +import subprocess +import time + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports +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): + def __init__(self): + super().__init__() + + self.name = "Autopairs" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms + + self.chars = { + "quotedbl": "\"", + "apostrophe": "'", + "parenleft": "(", + "bracketleft": "[", + "braceleft": "{", + "less": "<", + "grave": "`", + } + + self.close = { + "\"": "\"", + "'": "'", + "(": ")", + "[": "]", + "{": "}", + "<": ">", + "`": "`", + } + + def generate_reference_ui_element(self): + ... + + def run(self): + ... + + def subscribe_to_events(self): + self._event_system.subscribe("set_active_src_view", self._set_active_src_view) + self._event_system.subscribe("autopairs", self._autopairs) + + def _set_active_src_view(self, source_view): + self._active_src_view = source_view + self._buffer = self._active_src_view.get_buffer() + self._tag_table = self._buffer.get_tag_table() + + def _buffer_changed_first_load(self, buffer): + self._do_colorize(buffer) + + + def _buffer_changed(self, buffer): + tag_table = buffer.get_tag_table() + mark = buffer.get_insert() + iter = buffer.get_iter_at_mark(mark) + tags = iter.get_tags() + + def _autopairs(self, keyval_name, ctrl, alt, shift): + if keyval_name in self.chars: + return self.text_insert(self._buffer, keyval_name) + elif ctrl and keyval_name == "Return": + self.move_to_next_line(self._buffer) + + # NOTE: All of below to EOF, lovingly taken from Hamad Al Marri's Gamma + # text editor. I did do some cleanup of comments but otherwise pretty + # much the same code just fitted to my plugin architecture. + # Link: https://gitlab.com/hamadmarri/gamma-text-editor + def move_to_next_line(self, buffer): + selection = buffer.get_selection_bounds() + if selection != (): return False + + position = buffer.get_iter_at_mark(buffer.get_insert()) + + if position.ends_line(): return False + + position.forward_to_line_end() + buffer.place_cursor(position) + + return False + + def text_insert(self, buffer, text): + selection = buffer.get_selection_bounds() + if selection == (): + return self.add_close(buffer, text, ) + else: + return self.add_enclose(buffer, text, selection) + + def add_close(self, buffer, text): + text = self.chars[text] + text += self.close[text] + + position = buffer.get_iter_at_mark(buffer.get_insert()) + + c = position.get_char() + if not c in (" ", "", ";", ":", "\t", ",", ".", "\n", "\r") \ + and not c in list(self.close.values()): + return False + + buffer.insert(position, text) + + position = buffer.get_iter_at_mark(buffer.get_insert()) + position.backward_char() + buffer.place_cursor(position) + + return True + + def add_enclose(self, buffer, text, selection): + (start, end) = selection + selected = buffer.get_text(start, end, False) + if len(selected) <= 3 and selected in ("<", ">", ">>>" + "<<", ">>", + "\"", "'", "`", + "(", ")", + "[", "]", + "{", "}", + "=", "==", + "!=", "==="): + return False + + start_mark = buffer.create_mark("startclose", start, False) + end_mark = buffer.create_mark("endclose", end, False) + + buffer.begin_user_action() + + t = self.chars[text] + buffer.insert(start, t) + end = buffer.get_iter_at_mark(end_mark) + t = self.close[t] + buffer.insert(end, t) + + start = buffer.get_iter_at_mark(start_mark) + end = buffer.get_iter_at_mark(end_mark) + end.backward_char() + buffer.select_range(start, end) + + buffer.end_user_action() + + return True diff --git a/src/core/widgets/base/sourceview/source_view.py b/src/core/widgets/base/sourceview/source_view.py index 9dd6345..2f98d24 100644 --- a/src/core/widgets/base/sourceview/source_view.py +++ b/src/core/widgets/base/sourceview/source_view.py @@ -170,6 +170,9 @@ class SourceView(SourceViewEventsMixin, GtkSource.View): return True + # NOTE: if a plugin recieves the call and handles, it will be the final decider for propigation + return event_system.emit_and_await("autopairs", (keyname, is_control, is_alt, is_shift)) + def _button_press_event(self, widget = None, eve = None, user_data = None): if eve.type == Gdk.EventType.BUTTON_PRESS and eve.button == 1 : # l-click