From 5273c58ed6b235b9e94e22735524a8269563d49a Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 14 Feb 2026 16:15:54 -0600
Subject: [PATCH] Major completion provider overhaul; pluigin load and pattern
improvements; css overhaul/cleanup; source view state modes added
---
plugins/lsp_completer/__init__.py | 3 +
plugins/lsp_completer/__main__.py | 3 +
plugins/lsp_completer/manifest.json | 7 +
plugins/lsp_completer/plugin.py | 43 ++
plugins/lsp_completer/provider.py | 79 +++
.../lsp_completer/provider_response_cache.py | 45 ++
plugins/snippets_completer/__init__.py | 3 +
plugins/snippets_completer/__main__.py | 3 +
plugins/snippets_completer/cson/__init__.py | 3 +
plugins/snippets_completer/cson/parser.py | 295 +++++++++
.../snippets_completer/cson/speg/__init__.py | 1 +
plugins/snippets_completer/cson/speg/peg.py | 147 +++++
plugins/snippets_completer/cson/writer.py | 191 ++++++
plugins/snippets_completer/manifest.json | 7 +
plugins/snippets_completer/plugin.py | 40 ++
plugins/snippets_completer/provider.py | 63 ++
.../provider_response_cache.py | 63 ++
plugins/snippets_completer/snippets.cson | 614 ++++++++++++++++++
src/core/controllers/base_controller.py | 17 +-
src/core/widgets/code/code_base.py | 9 +-
.../code/command_system/command_system.py | 4 +-
.../commands/show_completion.py | 12 +-
.../example_completion_provider.py | 84 ---
.../lsp_completion_provider.py | 137 ----
.../provider_response_cache_base.py | 87 +++
.../python_completion_provider.py | 107 ---
src/core/widgets/code/controllers/__init__.py | 2 +-
.../code/controllers/completion_controller.py | 133 ++--
.../controllers/source_views_controller.py | 128 ----
.../code/controllers/views/__init__.py | 10 +
.../code/controllers/views/signal_mapper.py | 63 ++
.../views/source_views_controller.py | 74 +++
.../code/controllers/views/state_manager.py | 65 ++
.../code/controllers/views/states/__init__.py | 8 +
.../views/states/source_view_command_state.py | 36 +
.../views/states/source_view_insert_state.py | 71 ++
.../states/source_view_multi_insert_state.py | 131 ++++
.../states/source_view_read_only_state.py | 36 +
src/core/widgets/code/key_mapper.py | 10 +
.../code/mixins/source_mark_events_mixin.py | 107 +++
src/core/widgets/code/source_buffer.py | 31 +-
src/core/widgets/code/source_file.py | 39 +-
src/core/widgets/code/source_view.py | 6 +-
src/core/widgets/webkit/webkit_ui.py | 1 +
src/libs/controllers/controller_base.py | 4 +-
src/libs/controllers/controller_manager.py | 2 +-
src/libs/dto/code/__init__.py | 3 +
src/libs/dto/code/register_provider_event.py | 19 +
src/libs/dto/code/saved_file_event.py | 13 +
src/libs/dto/code/text_inserted_event.py | 11 +-
src/libs/dto/states/__init__.py | 7 +
src/libs/dto/states/cursor_action.py | 14 +
src/libs/dto/states/move_direction.py | 15 +
src/libs/dto/states/source_view_states.py | 14 +
src/libs/settings/other/webkit_ui_settings.py | 3 +-
src/plugins/controller.py | 4 +-
src/plugins/manifest_manager.py | 5 +-
src/plugins/plugin_types/__init__.py | 7 +
src/plugins/plugin_types/plugin_base.py | 49 ++
src/plugins/plugin_types/plugin_code.py | 41 ++
.../plugin_ui.py} | 15 +-
src/plugins/plugins_controller_mixin.py | 4 +
user_config/usr/share/app_name/stylesheet.css | 235 ++-----
63 files changed, 2738 insertions(+), 745 deletions(-)
create mode 100644 plugins/lsp_completer/__init__.py
create mode 100644 plugins/lsp_completer/__main__.py
create mode 100644 plugins/lsp_completer/manifest.json
create mode 100644 plugins/lsp_completer/plugin.py
create mode 100644 plugins/lsp_completer/provider.py
create mode 100644 plugins/lsp_completer/provider_response_cache.py
create mode 100644 plugins/snippets_completer/__init__.py
create mode 100644 plugins/snippets_completer/__main__.py
create mode 100644 plugins/snippets_completer/cson/__init__.py
create mode 100644 plugins/snippets_completer/cson/parser.py
create mode 100644 plugins/snippets_completer/cson/speg/__init__.py
create mode 100644 plugins/snippets_completer/cson/speg/peg.py
create mode 100644 plugins/snippets_completer/cson/writer.py
create mode 100644 plugins/snippets_completer/manifest.json
create mode 100644 plugins/snippets_completer/plugin.py
create mode 100644 plugins/snippets_completer/provider.py
create mode 100644 plugins/snippets_completer/provider_response_cache.py
create mode 100644 plugins/snippets_completer/snippets.cson
delete mode 100644 src/core/widgets/code/completion_providers/example_completion_provider.py
delete mode 100644 src/core/widgets/code/completion_providers/lsp_completion_provider.py
create mode 100644 src/core/widgets/code/completion_providers/provider_response_cache_base.py
delete mode 100644 src/core/widgets/code/completion_providers/python_completion_provider.py
delete mode 100644 src/core/widgets/code/controllers/source_views_controller.py
create mode 100644 src/core/widgets/code/controllers/views/__init__.py
create mode 100644 src/core/widgets/code/controllers/views/signal_mapper.py
create mode 100644 src/core/widgets/code/controllers/views/source_views_controller.py
create mode 100644 src/core/widgets/code/controllers/views/state_manager.py
create mode 100644 src/core/widgets/code/controllers/views/states/__init__.py
create mode 100644 src/core/widgets/code/controllers/views/states/source_view_command_state.py
create mode 100644 src/core/widgets/code/controllers/views/states/source_view_insert_state.py
create mode 100644 src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py
create mode 100644 src/core/widgets/code/controllers/views/states/source_view_read_only_state.py
create mode 100644 src/core/widgets/code/mixins/source_mark_events_mixin.py
create mode 100644 src/libs/dto/code/register_provider_event.py
create mode 100644 src/libs/dto/code/saved_file_event.py
create mode 100644 src/libs/dto/states/__init__.py
create mode 100644 src/libs/dto/states/cursor_action.py
create mode 100644 src/libs/dto/states/move_direction.py
create mode 100644 src/libs/dto/states/source_view_states.py
create mode 100644 src/plugins/plugin_types/__init__.py
create mode 100644 src/plugins/plugin_types/plugin_base.py
create mode 100644 src/plugins/plugin_types/plugin_code.py
rename src/plugins/{plugin_base.py => plugin_types/plugin_ui.py} (68%)
diff --git a/plugins/lsp_completer/__init__.py b/plugins/lsp_completer/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/lsp_completer/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/lsp_completer/__main__.py b/plugins/lsp_completer/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/lsp_completer/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/lsp_completer/manifest.json b/plugins/lsp_completer/manifest.json
new file mode 100644
index 0000000..6557297
--- /dev/null
+++ b/plugins/lsp_completer/manifest.json
@@ -0,0 +1,7 @@
+{
+ "name": "LSP Completer",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {}
+}
diff --git a/plugins/lsp_completer/plugin.py b/plugins/lsp_completer/plugin.py
new file mode 100644
index 0000000..7de452e
--- /dev/null
+++ b/plugins/lsp_completer/plugin.py
@@ -0,0 +1,43 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from libs.dto.base_event import BaseEvent
+from libs.event_factory import Event_Factory
+
+from plugins.plugin_types import PluginCode
+
+from .provider import Provider
+
+
+
+class Plugin(PluginCode):
+ def __init__(self):
+ super(Plugin, self).__init__()
+
+ self.provider: Provider = None
+
+
+ def _controller_message(self, event: BaseEvent):
+ ...
+
+ def load(self):
+ self.provider = Provider()
+
+ event = Event_Factory.create_event(
+ "register_provider",
+ provider_name = "LSP Completer",
+ provider = self.provider,
+ language_ids = []
+ )
+ self.message_to("completion", event)
+
+ def run(self):
+ ...
+
+ def generate_plugin_element(self):
+ ...
diff --git a/plugins/lsp_completer/provider.py b/plugins/lsp_completer/provider.py
new file mode 100644
index 0000000..305d057
--- /dev/null
+++ b/plugins/lsp_completer/provider.py
@@ -0,0 +1,79 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GtkSource
+from gi.repository import GObject
+
+# Application imports
+from .provider_response_cache import ProviderResponseCache
+
+
+
+class Provider(GObject.Object, GtkSource.CompletionProvider):
+ """
+ This code is an LSP code completion plugin for Newton.
+ # NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
+ """
+ __gtype_name__ = 'LSPProvider'
+
+ def __init__(self):
+ GObject.Object.__init__(self)
+
+ self.response_cache: ProviderResponseCache = ProviderResponseCache()
+
+
+ def pre_populate(self, context):
+ ...
+
+ def do_get_name(self):
+ return "LSP Code Completion"
+
+ def get_iter_correctly(self, context):
+ return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
+
+ def do_match(self, context):
+ word = self.response_cache.get_word(context)
+ if not word or len(word) < 2: return False
+
+ iter = self.get_iter_correctly(context)
+ iter.backward_char()
+ ch = iter.get_char()
+ # NOTE: Look to re-add or apply supprting logic to use spaces
+ # As is it slows down the editor in certain contexts...
+ # if not (ch in ('_', '.', ' ') or ch.isalnum()):
+ if not (ch in ('_', '.') or ch.isalnum()):
+ return False
+
+ buffer = iter.get_buffer()
+ if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
+ return False
+
+ return True
+
+ def do_get_priority(self):
+ return 5
+
+ def do_get_activation(self):
+ """ The context for when a provider will show results """
+ # return GtkSource.CompletionActivation.NONE
+ return GtkSource.CompletionActivation.USER_REQUESTED
+ # return GtkSource.CompletionActivation.INTERACTIVE
+
+ def do_populate(self, context):
+ proposals = self.get_completion_filter(context)
+
+ context.add_proposals(self, proposals, True)
+
+ def get_completion_filter(self, context):
+ proposals = [
+ self.response_cache.create_completion_item(
+ "LSP Class",
+ "LSP Code",
+ "A test LSP completion item..."
+ )
+ ]
+
+ return proposals
diff --git a/plugins/lsp_completer/provider_response_cache.py b/plugins/lsp_completer/provider_response_cache.py
new file mode 100644
index 0000000..54b827d
--- /dev/null
+++ b/plugins/lsp_completer/provider_response_cache.py
@@ -0,0 +1,45 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GtkSource
+
+# Application imports
+from libs.event_factory import Code_Event_Types
+
+from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
+
+
+
+class ProviderResponseCache(ProviderResponseCacheBase):
+ def __init__(self):
+ super(ProviderResponseCache, self).__init__()
+
+
+ def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
+ ...
+
+ def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
+ ...
+
+ def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
+ ...
+
+ def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
+ ...
+
+ def filter(self, word: str):
+ ...
+
+ def filter_with_context(self, context: GtkSource.CompletionContext):
+ proposals = [
+ self.create_completion_item(
+ "LSP Class",
+ "LSP Code",
+ "A test LSP completion item..."
+ )
+ ]
+
+ return proposals
diff --git a/plugins/snippets_completer/__init__.py b/plugins/snippets_completer/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/snippets_completer/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/snippets_completer/__main__.py b/plugins/snippets_completer/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/snippets_completer/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/snippets_completer/cson/__init__.py b/plugins/snippets_completer/cson/__init__.py
new file mode 100644
index 0000000..7eaf7bf
--- /dev/null
+++ b/plugins/snippets_completer/cson/__init__.py
@@ -0,0 +1,3 @@
+from .parser import load, loads
+from .writer import dump, dumps
+from .speg import ParseError
diff --git a/plugins/snippets_completer/cson/parser.py b/plugins/snippets_completer/cson/parser.py
new file mode 100644
index 0000000..b8e4e98
--- /dev/null
+++ b/plugins/snippets_completer/cson/parser.py
@@ -0,0 +1,295 @@
+from .speg import peg
+import re, sys
+
+if sys.version_info[0] == 2:
+ _chr = unichr
+else:
+ _chr = chr
+
+def load(fin):
+ return loads(fin.read())
+
+def loads(s):
+ if isinstance(s, bytes):
+ s = s.decode('utf-8')
+ if s.startswith(u'\ufeff'):
+ s = s[1:]
+ return peg(s.replace('\r\n', '\n'), _p_root)
+
+def _p_ws(p):
+ p('[ \t]*')
+
+def _p_nl(p):
+ p(r'([ \t]*(?:#[^\n]*)?\r?\n)+')
+
+def _p_ews(p):
+ with p:
+ p(_p_nl)
+ p(_p_ws)
+
+def _p_id(p):
+ return p(r'[$a-zA-Z_][$0-9a-zA-Z_]*')
+
+_escape_table = {
+ 'r': '\r',
+ 'n': '\n',
+ 't': '\t',
+ 'f': '\f',
+ 'b': '\b',
+}
+def _p_unescape(p):
+ esc = p('\\\\(?:u[0-9a-fA-F]{4}|[^\n])')
+ if esc[1] == 'u':
+ return _chr(int(esc[2:], 16))
+ return _escape_table.get(esc[1:], esc[1:])
+
+_re_indent = re.compile(r'[ \t]*')
+def _p_block_str(p, c):
+ p(r'{c}{c}{c}'.format(c=c))
+ lines = [['']]
+ with p:
+ while True:
+ s = p(r'(?:{c}(?!{c}{c})|[^{c}\\])*'.format(c=c))
+ l = s.split('\n')
+ lines[-1].append(l[0])
+ lines.extend([x] for x in l[1:])
+ if p(r'(?:\\\n[ \t]*)*'):
+ continue
+ p.commit()
+ lines[-1].append(p(_p_unescape))
+ p(r'{c}{c}{c}'.format(c=c))
+
+ lines = [''.join(l) for l in lines]
+ strip_ws = len(lines) > 1
+ if strip_ws and all(c in ' \t' for c in lines[-1]):
+ lines.pop()
+
+ indent = None
+ for line in lines[1:]:
+ if not line:
+ continue
+ if indent is None:
+ indent = _re_indent.match(line).group(0)
+ continue
+ for i, (c1, c2) in enumerate(zip(indent, line)):
+ if c1 != c2:
+ indent = indent[:i]
+ break
+
+ ind_len = len(indent or '')
+ if strip_ws and all(c in ' \t' for c in lines[0]):
+ lines = [line[ind_len:] for line in lines[1:]]
+ else:
+ lines[1:] = [line[ind_len:] for line in lines[1:]]
+
+ return '\n'.join(lines)
+
+_re_mstr_nl = re.compile(r'(?:[ \t]*\n)+[ \t]*')
+_re_mstr_trailing_nl = re.compile(_re_mstr_nl.pattern + r'\Z')
+def _p_multiline_str(p, c):
+ p('{c}(?!{c}{c})(?:[ \t]*\n[ \t]*)?'.format(c=c))
+ string_parts = []
+ with p:
+ while True:
+ string_parts.append(p(r'[^{c}\\]*'.format(c=c)))
+ if p(r'(?:\\\n[ \t]*)*'):
+ string_parts.append('')
+ continue
+ p.commit()
+ string_parts.append(p(_p_unescape))
+ p(c)
+ string_parts[-1] = _re_mstr_trailing_nl.sub('', string_parts[-1])
+ string_parts[::2] = [_re_mstr_nl.sub(' ', part) for part in string_parts[::2]]
+ return ''.join(string_parts)
+
+def _p_string(p):
+ with p:
+ return p(_p_block_str, '"')
+ with p:
+ return p(_p_block_str, "'")
+ with p:
+ return p(_p_multiline_str, '"')
+ return p(_p_multiline_str, "'")
+
+def _p_array_value(p):
+ with p:
+ p(_p_nl)
+ return p(_p_object)
+ with p:
+ p(_p_ws)
+ return p(_p_line_object)
+ p(_p_ews)
+ return p(_p_simple_value)
+
+def _p_key(p):
+ with p:
+ return p(_p_id)
+ return p(_p_string)
+
+def _p_flow_kv(p):
+ k = p(_p_key)
+ p(_p_ews)
+ p(':')
+ with p:
+ p(_p_nl)
+ return k, p(_p_object)
+ with p:
+ p(_p_ws)
+ return k, p(_p_line_object)
+ p(_p_ews)
+ return k, p(_p_simple_value)
+
+def _p_flow_obj_sep(p):
+ with p:
+ p(_p_ews)
+ p(',')
+ p(_p_ews)
+ return
+
+ p(_p_nl)
+ p(_p_ws)
+
+def _p_simple_value(p):
+ with p:
+ p('null')
+ return None
+
+ with p:
+ p('false')
+ return False
+ with p:
+ p('true')
+ return True
+
+ with p:
+ return int(p('0b[01]+')[2:], 2)
+ with p:
+ return int(p('0o[0-7]+')[2:], 8)
+ with p:
+ return int(p('0x[0-9a-fA-F]+')[2:], 16)
+ with p:
+ return float(p(r'-?(?:[1-9][0-9]*|0)?\.[0-9]+(?:[Ee][\+-]?[0-9]+)?|(?:[1-9][0-9]*|0)(?:\.[0-9]+)?[Ee][\+-]?[0-9]+'))
+ with p:
+ return int(p('-?[1-9][0-9]*|0'), 10)
+
+ with p:
+ return p(_p_string)
+
+ with p:
+ p(r'\[')
+ r = []
+ with p:
+ p.set('I', '')
+ r.append(p(_p_array_value))
+ with p:
+ while True:
+ with p:
+ p(_p_ews)
+ p(',')
+ rr = p(_p_array_value)
+ if not p:
+ p(_p_nl)
+ with p:
+ rr = p(_p_object)
+ if not p:
+ p(_p_ews)
+ rr = p(_p_simple_value)
+ r.append(rr)
+ p.commit()
+ with p:
+ p(_p_ews)
+ p(',')
+ p(_p_ews)
+ p(r'\]')
+ return r
+
+ p(r'\{')
+
+ r = {}
+ p(_p_ews)
+ with p:
+ p.set('I', '')
+ k, v = p(_p_flow_kv)
+ r[k] = v
+ with p:
+ while True:
+ p(_p_flow_obj_sep)
+ k, v = p(_p_flow_kv)
+ r[k] = v
+ p.commit()
+ p(_p_ews)
+ with p:
+ p(',')
+ p(_p_ews)
+ p(r'\}')
+ return r
+
+def _p_line_kv(p):
+ k = p(_p_key)
+ p(_p_ws)
+ p(':')
+ p(_p_ws)
+ with p:
+ p(_p_nl)
+ p(p.get('I'))
+ return k, p(_p_indented_object)
+ with p:
+ return k, p(_p_line_object)
+ with p:
+ return k, p(_p_simple_value)
+ p(_p_nl)
+ p(p.get('I'))
+ p('[ \t]')
+ p(_p_ws)
+ return k, p(_p_simple_value)
+
+def _p_line_object(p):
+ k, v = p(_p_line_kv)
+ r = { k: v }
+ with p:
+ while True:
+ p(_p_ws)
+ p(',')
+ p(_p_ws)
+ k, v = p(_p_line_kv)
+ r[k] = v # uniqueness
+ p.commit()
+ return r
+
+def _p_object(p):
+ p.set('I', p.get('I') + p('[ \t]*'))
+ r = p(_p_line_object)
+ with p:
+ while True:
+ p(_p_ws)
+ with p:
+ p(',')
+ p(_p_nl)
+ p(p.get('I'))
+ rr = p(_p_line_object)
+ r.update(rr) # unqueness
+ p.commit()
+ return r
+
+def _p_indented_object(p):
+ p.set('I', p.get('I') + p('[ \t]'))
+ return p(_p_object)
+
+def _p_root(p):
+ with p:
+ p(_p_nl)
+
+ with p:
+ p.set('I', '')
+ r = p(_p_object)
+ p(_p_ws)
+ with p:
+ p(',')
+
+ if not p:
+ p(_p_ws)
+ r = p(_p_simple_value)
+
+ p(_p_ews)
+ p(p.eof)
+ return r
diff --git a/plugins/snippets_completer/cson/speg/__init__.py b/plugins/snippets_completer/cson/speg/__init__.py
new file mode 100644
index 0000000..d3ebb8f
--- /dev/null
+++ b/plugins/snippets_completer/cson/speg/__init__.py
@@ -0,0 +1 @@
+from .peg import peg, ParseError
diff --git a/plugins/snippets_completer/cson/speg/peg.py b/plugins/snippets_completer/cson/speg/peg.py
new file mode 100644
index 0000000..15bd1d1
--- /dev/null
+++ b/plugins/snippets_completer/cson/speg/peg.py
@@ -0,0 +1,147 @@
+import sys, re
+
+class ParseError(Exception):
+ def __init__(self, msg, text, offset, line, col):
+ self.msg = msg
+ self.text = text
+ self.offset = offset
+ self.line = line
+ self.col = col
+ super(ParseError, self).__init__(msg, offset, line, col)
+
+if sys.version_info[0] == 2:
+ _basestr = basestring
+else:
+ _basestr = str
+
+def peg(s, r):
+ p = _Peg(s)
+ try:
+ return p(r)
+ except _UnexpectedError as e:
+ offset = max(p._errors)
+ err = p._errors[offset]
+ raise ParseError(err.msg, s, offset, err.line, err.col)
+
+class _UnexpectedError(RuntimeError):
+ def __init__(self, state, expr):
+ self.state = state
+ self.expr = expr
+
+class _PegState:
+ def __init__(self, pos, line, col):
+ self.pos = pos
+ self.line = line
+ self.col = col
+ self.vars = {}
+ self.commited = False
+
+class _PegError:
+ def __init__(self, msg, line, col):
+ self.msg = msg
+ self.line = line
+ self.col = col
+
+class _Peg:
+ def __init__(self, s):
+ self._s = s
+ self._states = [_PegState(0, 1, 1)]
+ self._errors = {}
+ self._re_cache = {}
+
+ def __call__(self, r, *args, **kw):
+ if isinstance(r, _basestr):
+ compiled = self._re_cache.get(r)
+ if not compiled:
+ compiled = re.compile(r)
+ self._re_cache[r] = compiled
+ st = self._states[-1]
+ m = compiled.match(self._s[st.pos:])
+ if not m:
+ self.error(expr=r, err=kw.get('err'))
+
+ ms = m.group(0)
+ st.pos += len(ms)
+ nl_pos = ms.rfind('\n')
+ if nl_pos < 0:
+ st.col += len(ms)
+ else:
+ st.col = len(ms) - nl_pos
+ st.line += ms[:nl_pos].count('\n') + 1
+ return ms
+ else:
+ kw.pop('err', None)
+ return r(self, *args, **kw)
+
+ def __repr__(self):
+ pos = self._states[-1].pos
+ vars = {}
+ for st in self._states:
+ vars.update(st.vars)
+ return '_Peg(%r, %r)' % (self._s[:pos] + '*' + self._s[pos:], vars)
+
+ @staticmethod
+ def eof(p):
+ if p._states[-1].pos != len(p._s):
+ p.error()
+
+ def error(self, err=None, expr=None):
+ st = self._states[-1]
+ if err is None:
+ err = 'expected {!r}, found {!r}'.format(expr, self._s[st.pos:st.pos+4])
+ self._errors[st.pos] = _PegError(err, st.line, st.col)
+ raise _UnexpectedError(st, expr)
+
+ def get(self, key, default=None):
+ for state in self._states[::-1]:
+ if key in state.vars:
+ return state.vars[key][0]
+ return default
+
+ def set(self, key, value):
+ self._states[-1].vars[key] = value, False
+
+ def set_global(self, key, value):
+ self._states[-1].vars[key] = value, True
+
+ def opt(self, *args, **kw):
+ with self:
+ return self(*args, **kw)
+
+ def not_(self, s, *args, **kw):
+ with self:
+ self(s)
+ self.error()
+
+ def __enter__(self):
+ self._states[-1].committed = False
+ self._states.append(_PegState(self._states[-1].pos, self._states[-1].line, self._states[-1].col))
+
+ def __exit__(self, type, value, traceback):
+ if type is None:
+ self.commit()
+ self._states.pop()
+ return type == _UnexpectedError
+
+ def commit(self):
+ cur = self._states[-1]
+ prev = self._states[-2]
+
+ for key in cur.vars:
+ val, g = cur.vars[key]
+ if not g:
+ continue
+ if key in prev.vars:
+ prev.vars[key] = val, prev.vars[key][1]
+ else:
+ prev.vars[key] = val, True
+
+ prev.pos = cur.pos
+ prev.line = cur.line
+ prev.col = cur.col
+ prev.committed = True
+
+ def __nonzero__(self):
+ return self._states[-1].committed
+
+ __bool__ = __nonzero__
diff --git a/plugins/snippets_completer/cson/writer.py b/plugins/snippets_completer/cson/writer.py
new file mode 100644
index 0000000..de856a4
--- /dev/null
+++ b/plugins/snippets_completer/cson/writer.py
@@ -0,0 +1,191 @@
+import re, json, sys
+
+if sys.version_info[0] == 2:
+ def _is_num(o):
+ return isinstance(o, int) or isinstance(o, long) or isinstance(o, float)
+ def _stringify(o):
+ if isinstance(o, str):
+ return unicode(o)
+ if isinstance(o, unicode):
+ return o
+ return None
+else:
+ def _is_num(o):
+ return isinstance(o, int) or isinstance(o, float)
+ def _stringify(o):
+ if isinstance(o, bytes):
+ return o.decode()
+ if isinstance(o, str):
+ return o
+ return None
+
+_id_re = re.compile(r'[$a-zA-Z_][$0-9a-zA-Z_]*\Z')
+
+class CSONEncoder:
+ def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
+ indent=None, default=None):
+ self._skipkeys = skipkeys
+ self._ensure_ascii = ensure_ascii
+ self._allow_nan = allow_nan
+ self._sort_keys = sort_keys
+ self._indent = ' ' * (indent or 4)
+ self._default = default
+ if check_circular:
+ self._obj_stack = set()
+ else:
+ self._obj_stack = None
+
+ def _format_simple_val(self, o):
+ if o is None:
+ return 'null'
+ if isinstance(o, bool):
+ return 'true' if o else 'false'
+ if _is_num(o):
+ return str(o)
+ s = _stringify(o)
+ if s is not None:
+ return self._escape_string(s)
+ return None
+
+ def _escape_string(self, s):
+ r = json.dumps(s, ensure_ascii=self._ensure_ascii)
+ return u"'{}'".format(r[1:-1].replace("'", r"\'"))
+
+ def _escape_key(self, s):
+ if s is None or isinstance(s, bool) or _is_num(s):
+ s = str(s)
+ s = _stringify(s)
+ if s is None:
+ if self._skipkeys:
+ return None
+ raise TypeError('keys must be a string')
+ if not _id_re.match(s):
+ return self._escape_string(s)
+ return s
+
+ def _push_obj(self, o):
+ if self._obj_stack is not None:
+ if id(o) in self._obj_stack:
+ raise ValueError('Circular reference detected')
+ self._obj_stack.add(id(o))
+
+ def _pop_obj(self, o):
+ if self._obj_stack is not None:
+ self._obj_stack.remove(id(o))
+
+ def _encode(self, o, obj_val=False, indent='', force_flow=False):
+ if isinstance(o, list):
+ if not o:
+ if obj_val:
+ yield ' []\n'
+ else:
+ yield indent
+ yield '[]\n'
+ else:
+ if obj_val:
+ yield ' [\n'
+ else:
+ yield indent
+ yield '[\n'
+ indent = indent + self._indent
+ self._push_obj(o)
+ for v in o:
+ for chunk in self._encode(v, obj_val=False, indent=indent, force_flow=True):
+ yield chunk
+ self._pop_obj(o)
+ yield indent[:-len(self._indent)]
+ yield ']\n'
+ elif isinstance(o, dict):
+ items = [(self._escape_key(k), v) for k, v in o.items()]
+ if self._skipkeys:
+ items = [(k, v) for k, v in items if k is not None]
+ if self._sort_keys:
+ items.sort()
+ if force_flow or not items:
+ if not items:
+ if obj_val:
+ yield ' {}\n'
+ else:
+ yield indent
+ yield '{}\n'
+ else:
+ if obj_val:
+ yield ' {\n'
+ else:
+ yield indent
+ yield '{\n'
+ indent = indent + self._indent
+ self._push_obj(o)
+ for k, v in items:
+ yield indent
+ yield k
+ yield ':'
+ for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
+ yield chunk
+ self._pop_obj(o)
+ yield indent[:-len(self._indent)]
+ yield '}\n'
+ else:
+ if obj_val:
+ yield '\n'
+ self._push_obj(o)
+ for k, v in items:
+ yield indent
+ yield k
+ yield ':'
+ for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
+ yield chunk
+ self._pop_obj(o)
+ else:
+ v = self._format_simple_val(o)
+ if v is None:
+ self._push_obj(o)
+ v = self.default(o)
+ for chunk in self._encode(v, obj_val=obj_val, indent=indent, force_flow=force_flow):
+ yield chunk
+ self._pop_obj(o)
+ else:
+ if obj_val:
+ yield ' '
+ else:
+ yield indent
+ yield v
+ yield '\n'
+
+ def iterencode(self, o):
+ return self._encode(o)
+
+ def encode(self, o):
+ return ''.join(self.iterencode(o))
+
+ def default(self, o):
+ if self._default is None:
+ raise TypeError('Cannot serialize an object of type {}'.format(type(o).__name__))
+ return self._default(o)
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None,
+ indent=None, default=None, sort_keys=False, **kw):
+ if indent is None and cls is None:
+ return json.dump(obj, fp, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
+ allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
+
+ if cls is None:
+ cls = CSONEncoder
+ encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
+ allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
+
+ for chunk in encoder.iterencode(obj):
+ fp.write(chunk)
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None,
+ default=None, sort_keys=False, **kw):
+ if indent is None and cls is None:
+ return json.dumps(obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
+ allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
+
+ if cls is None:
+ cls = CSONEncoder
+ encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
+ allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
+
+ return encoder.encode(obj)
diff --git a/plugins/snippets_completer/manifest.json b/plugins/snippets_completer/manifest.json
new file mode 100644
index 0000000..669678e
--- /dev/null
+++ b/plugins/snippets_completer/manifest.json
@@ -0,0 +1,7 @@
+{
+ "name": "Snippets Completer",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {}
+}
diff --git a/plugins/snippets_completer/plugin.py b/plugins/snippets_completer/plugin.py
new file mode 100644
index 0000000..100c641
--- /dev/null
+++ b/plugins/snippets_completer/plugin.py
@@ -0,0 +1,40 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from libs.dto.base_event import BaseEvent
+from libs.event_factory import Event_Factory
+
+from plugins.plugin_types import PluginCode
+
+from .provider import Provider
+
+
+
+class Plugin(PluginCode):
+ def __init__(self):
+ super(Plugin, self).__init__()
+
+ self.provider: Provider = None
+
+
+ def _controller_message(self, event: BaseEvent):
+ ...
+
+ def load(self):
+ self.provider = Provider()
+
+ event = Event_Factory.create_event(
+ "register_provider",
+ provider_name = "Snippets Completer",
+ provider = self.provider,
+ language_ids = []
+ )
+ self.message_to("completion", event)
+
+ def run(self):
+ ...
diff --git a/plugins/snippets_completer/provider.py b/plugins/snippets_completer/provider.py
new file mode 100644
index 0000000..9ae0c45
--- /dev/null
+++ b/plugins/snippets_completer/provider.py
@@ -0,0 +1,63 @@
+# Python imports
+import re
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import GtkSource
+
+# Application imports
+from .provider_response_cache import ProviderResponseCache
+
+
+
+class Provider(GObject.GObject, GtkSource.CompletionProvider):
+ """
+ This is a Snippits Completion Provider.
+ # NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
+ """
+ __gtype_name__ = 'SnippetsCompletionProvider'
+
+ def __init__(self):
+ GObject.Object.__init__(self)
+
+ self.response_cache: ProviderResponseCache = ProviderResponseCache()
+
+
+ def do_get_name(self):
+ return 'Snippits Completion'
+
+ def do_match(self, context):
+ word = self.response_cache.get_word(context)
+ if not word or len(word) < 2: return False
+
+ return True
+
+ def do_get_priority(self):
+ return 2
+
+ def do_get_activation(self):
+ """ The context for when a provider will show results """
+ # return GtkSource.CompletionActivation.NONE
+ return GtkSource.CompletionActivation.USER_REQUESTED
+ # return GtkSource.CompletionActivation.INTERACTIVE
+
+ def do_populate(self, context):
+ word = self.response_cache.get_word(context)
+ results = self.response_cache.filter(word)
+ proposals = []
+
+ for entry in results:
+ proposals.append(
+ self.response_cache.create_completion_item(
+ entry["label"],
+ entry["text"],
+ entry["info"]
+ )
+ )
+
+ context.add_proposals(self, proposals, True)
diff --git a/plugins/snippets_completer/provider_response_cache.py b/plugins/snippets_completer/provider_response_cache.py
new file mode 100644
index 0000000..656d703
--- /dev/null
+++ b/plugins/snippets_completer/provider_response_cache.py
@@ -0,0 +1,63 @@
+# Python imports
+from os import path
+
+# Lib imports
+import gi
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GLib
+from gi.repository import GtkSource
+
+# Application imports
+from libs.event_factory import Code_Event_Types
+
+from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
+
+from . import cson
+
+
+
+class ProviderResponseCache(ProviderResponseCacheBase):
+ def __init__(self):
+ super(ProviderResponseCache, self).__init__()
+
+ self.matchers: dict = {}
+
+ self.load_snippets()
+
+
+ def load_snippets(self):
+ fpath = path.join(path.dirname(path.realpath(__file__)), "snippets.cson")
+ with open(fpath, 'rb') as f:
+ self.snippets = cson.load(f)
+ for group in self.snippets:
+ self.snippets[group]
+ for entry in self.snippets[group]:
+ data = self.snippets[group][entry]
+ self.matchers[ data["prefix"] ] = {
+ "label": entry,
+ "text": data["body"],
+ "info": GLib.markup_escape_text( data["body"] )
+ }
+
+ def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
+ ...
+
+ def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
+ ...
+
+ def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
+ ...
+
+ def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
+ ...
+
+ def filter(self, word: str):
+ response: list = []
+
+ for entry in self.matchers:
+ if not word in entry: continue
+ data = self.matchers[entry]
+ response.append(data)
+
+ return response
diff --git a/plugins/snippets_completer/snippets.cson b/plugins/snippets_completer/snippets.cson
new file mode 100644
index 0000000..7f99623
--- /dev/null
+++ b/plugins/snippets_completer/snippets.cson
@@ -0,0 +1,614 @@
+# Your snippets
+#
+# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
+# expand the prefix into a larger code block with templated values.
+#
+# You can create a new snippet in this file by typing "snip" and then hitting
+# tab.
+#
+# An example CoffeeScript snippet to expand log to console.log:
+#
+# '.source.coffee':
+# 'Console log':
+# 'prefix': 'log'
+# 'body': 'console.log $1'
+#
+# Each scope (e.g. '.source.coffee' above) can only be declared once.
+#
+# This file uses CoffeeScript Object Notation (CSON).
+# If you are unfamiliar with CSON, you can read more about it in the
+# Atom Flight Manual:
+# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
+
+
+### HTML SNIPPETS ###
+'.text.html.basic':
+
+ 'HTML Template':
+ 'prefix': 'html'
+ 'body': """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+ 'Canvas Tag':
+ 'prefix': 'canvas'
+ 'body': """"""
+
+ 'Img Tag':
+ 'prefix': 'img'
+ 'body': """
"""
+
+ 'Br Tag':
+ 'prefix': 'br'
+ 'body': """
"""
+
+ 'Hr Tag':
+ 'prefix': 'hr'
+ 'body': """
"""
+
+ 'Server Side Events':
+ 'prefix': 'sse'
+ 'body': """// SSE events if supported
+if(typeof(EventSource) !== "undefined") {
+ let source = new EventSource("resources/php/sse.php");
+ source.onmessage = (event) => {
+ if (event.data === "") {
+ // code here
+ }
+ };
+} else {
+ console.log("SSE Not Supported In Browser...");
+}
+"""
+
+ 'AJAX Template Function':
+ 'prefix': 'ajax template'
+ 'body': """const doAjax = async (actionPath, data) => {
+ let xhttp = new XMLHttpRequest();
+
+ xhttp.onreadystatechange = function() {
+ if (this.readyState === 4 && this.status === 200) {
+ if (this.responseText != null) { // this.responseXML if getting XML fata
+ handleReturnData(JSON.parse(this.responseText));
+ } else {
+ console.log("No content returned. Check the file path.");
+ }
+ }
+ };
+
+ xhttp.open("POST", actionPath, true);
+ xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ // Force return to be JSON NOTE: Use application/xml to force XML
+ xhttp.overrideMimeType('application/json');
+ xhttp.send(data);
+}
+"""
+
+ 'CSS Message Colors':
+ 'prefix': 'css colors'
+ 'body': """.error { color: rgb(255, 0, 0); }
+ .warning { color: rgb(255, 168, 0); }
+ .success { color: rgb(136, 204, 39); }
+ """
+
+
+### JS SNIPPETS ###
+'.source.js':
+
+ 'Server Side Events':
+ 'prefix': 'sse'
+ 'body': """// SSE events if supported
+if(typeof(EventSource) !== "undefined") {
+ let source = new EventSource("resources/php/sse.php");
+ source.onmessage = (event) => {
+ if (event.data === "") {
+ // code here
+ }
+ };
+} else {
+ console.log("SSE Not Supported In Browser...");
+}
+"""
+
+ 'AJAX Template Function':
+ 'prefix': 'ajax template'
+ 'body': """const doAjax = async (actionPath, data) => {
+ let xhttp = new XMLHttpRequest();
+
+ xhttp.onreadystatechange = function() {
+ if (this.readyState === 4 && this.status === 200) {
+ if (this.responseText != null) { // this.responseXML if getting XML fata
+ handleReturnData(JSON.parse(this.responseText));
+ } else {
+ console.log("No content returned. Check the file path.");
+ }
+ }
+ };
+
+ xhttp.open("POST", actionPath, true);
+ xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ // Force return to be JSON NOTE: Use application/xml to force XML
+ xhttp.overrideMimeType('application/json');
+ xhttp.send(data);
+}
+"""
+
+ 'SE6 Function':
+ 'prefix': 'function se6'
+ 'body': """const funcName = (arg = "") => {
+
+ }
+"""
+
+### CSS SNIPPETS ###
+'.source.css':
+
+ 'CSS Message Colors':
+ 'prefix': 'css colors'
+ 'body': """.error { color: rgb(255, 0, 0); }
+ .warning { color: rgb(255, 168, 0); }
+ .success { color: rgb(136, 204, 39); }
+ """
+
+### PHP SNIPPETS ###
+'.text.html.php':
+
+ 'SSE PHP':
+ 'prefix': 'sse php'
+ 'body': """
+ """
+
+ 'PHP Template':
+ 'prefix': 'php'
+ 'body': """ Illegal Access Method!";
+ serverMessage("error", $message);
+}
+?>
+"""
+ 'HTML Template':
+ 'prefix': 'html'
+ 'body': """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+
+### BASH SNIPPETS ###
+'.source.shell':
+
+ 'Bash or Shell Template':
+ 'prefix': 'bash template'
+ 'body': """#!/bin/bash
+
+# . CONFIG.sh
+
+# set -o xtrace ## To debug scripts
+# set -o errexit ## To exit on error
+# set -o errunset ## To exit if a variable is referenced but not set
+
+
+function main() {
+ cd "$(dirname "$0")"
+ echo "Working Dir: " $(pwd)
+
+ file="$1"
+ if [ -z "${file}" ]; then
+ echo "ERROR: No file argument supplied..."
+ exit
+ fi
+
+ if [[ -f "${file}" ]]; then
+ echo "SUCCESS: The path and file exists!"
+ else
+ echo "ERROR: The path or file '${file}' does NOT exist..."
+ fi
+}
+main "$@";
+ """
+
+
+ 'Bash or Shell Config':
+ 'prefix': 'bash config'
+ 'body': """#!/bin/bash
+
+ shopt -s expand_aliases
+
+ alias echo="echo -e"
+ """
+
+
+### PYTHON SNIPPETS ###
+'.source.python':
+
+ 'Glade __main__ Class Template':
+ 'prefix': 'glade __main__ class'
+ 'body': """#!/usr/bin/python3
+
+
+# Python imports
+import argparse
+import faulthandler
+import traceback
+import signal
+from setproctitle import setproctitle
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import GLib
+
+# Application imports
+from app import Application
+
+
+def run():
+ try:
+ setproctitle('')
+ faulthandler.enable() # For better debug info
+
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
+
+ parser = argparse.ArgumentParser()
+ # Add long and short arguments
+ parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
+ parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
+ parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
+
+ parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
+ parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
+
+ # Read arguments (If any...)
+ args, unknownargs = parser.parse_known_args()
+
+ main = Application(args, unknownargs)
+ Gtk.main()
+ except Exception as e:
+ traceback.print_exc()
+ quit()
+
+
+if __name__ == "__main__":
+ ''' Set process title, get arguments, and create GTK main thread. '''
+ run()
+
+
+ """
+
+
+ 'Glade __main__ Testing Template':
+ 'prefix': 'glade testing class'
+ 'body': """#!/usr/bin/python3
+
+
+# Python imports
+import traceback
+import faulthandler
+import signal
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import GLib
+
+# Application imports
+
+
+app_name = "Gtk Quick Test"
+
+
+class Application(Gtk.ApplicationWindow):
+ def __init__(self):
+ super(Application, self).__init__()
+ self._setup_styling()
+ self._setup_signals()
+ self._load_widgets()
+
+ self.add(Gtk.Box())
+
+ self.show_all()
+
+
+ def _setup_styling(self):
+ self.set_default_size(1670, 830)
+ self.set_title(f"{app_name}")
+ # self.set_icon_from_file( settings.get_window_icon() )
+ self.set_gravity(5) # 5 = CENTER
+ self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
+
+ def _setup_signals(self):
+ self.connect("delete-event", Gtk.main_quit)
+
+
+ def _load_widgets(self):
+ ...
+
+
+
+
+def run():
+ try:
+ faulthandler.enable() # For better debug info
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
+
+ main = Application()
+ Gtk.main()
+ except Exception as e:
+ traceback.print_exc()
+ quit()
+
+
+if __name__ == "__main__":
+ ''' Set process title, get arguments, and create GTK main thread. '''
+ run()
+
+
+ """
+
+
+ 'Glade _init_ Class Template':
+ 'prefix': 'glade __init__ class'
+ 'body': """# Python imports
+import inspect
+
+
+# Lib imports
+
+
+# Application imports
+from utils import Settings
+from signal_classes import CrossClassSignals
+
+
+class Main:
+ def __init__(self, args):
+ settings = Settings()
+ builder = settings.returnBuilder()
+
+ # Gets the methods from the classes and sets to handler.
+ # Then, builder connects to any signals it needs.
+ classes = [CrossClassSignals(settings)]
+
+ handlers = {}
+ for c in classes:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+
+ builder.connect_signals(handlers)
+ window = settings.createWindow()
+ window.show()
+
+ """
+
+ 'Class Method':
+ 'prefix': 'def1'
+ 'body': """
+ def fname(self):
+ ...
+ """
+
+ 'Gtk Class Method':
+ 'prefix': 'def2'
+ 'body': """
+ def fname(self, widget, eve):
+ ...
+ """
+
+
+ 'Python Glade Settings Template':
+ 'prefix': 'glade settings class'
+ 'body': """# Python imports
+ import os
+
+ # Lib imports
+ import gi, cairo
+ gi.require_version('Gtk', '3.0')
+ gi.require_version('Gdk', '3.0')
+
+ from gi.repository import Gtk
+ from gi.repository import Gdk
+
+
+ # Application imports
+
+
+ class Settings:
+ def __init__(self):
+ self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
+ self.builder = Gtk.Builder()
+ self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade")
+
+ # 'Filters'
+ self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm',
+ '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
+ self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv',
+ '.mpeg', '.mp4', '.webm')
+ self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
+ self.music = ('.psf', '.mp3', '.ogg' , '.flac')
+ self.images = ('.png', '.jpg', '.jpeg', '.gif')
+ self.pdf = ('.pdf')
+
+
+ def createWindow(self):
+ # Get window and connect signals
+ window = self.builder.get_object("Main_Window")
+ window.connect("delete-event", gtk.main_quit)
+ self.setWindowData(window, False)
+ return window
+
+ def setWindowData(self, window, paintable):
+ screen = window.get_screen()
+ visual = screen.get_rgba_visual()
+
+ if visual != None and screen.is_composited():
+ window.set_visual(visual)
+
+ # bind css file
+ cssProvider = gtk.CssProvider()
+ cssProvider.load_from_path(self.SCRIPT_PTH + '../resources/stylesheet.css')
+ screen = Gdk.Screen.get_default()
+ styleContext = Gtk.StyleContext()
+ styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ window.set_app_paintable(paintable)
+ if paintable:
+ window.connect("draw", self.area_draw)
+
+ def getMonitorData(self):
+ screen = self.builder.get_object("Main_Window").get_screen()
+ monitors = []
+ for m in range(screen.get_n_monitors()):
+ monitors.append(screen.get_monitor_geometry(m))
+
+ for monitor in monitors:
+ print(str(monitor.width) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
+
+ return monitors
+
+ def area_draw(self, widget, cr):
+ cr.set_source_rgba(0, 0, 0, 0.54)
+ cr.set_operator(cairo.OPERATOR_SOURCE)
+ cr.paint()
+ cr.set_operator(cairo.OPERATOR_OVER)
+
+
+ def returnBuilder(self): return self.builder
+
+ # Filter returns
+ def returnOfficeFilter(self): return self.office
+ def returnVidsFilter(self): return self.vids
+ def returnTextFilter(self): return self.txt
+ def returnMusicFilter(self): return self.music
+ def returnImagesFilter(self): return self.images
+ def returnPdfFilter(self): return self.pdf
+
+ """
+
+ 'Python Glade CrossClassSignals Template':
+ 'prefix': 'glade crossClassSignals class'
+ 'body': """# Python imports
+ import threading
+ import subprocess
+ import os
+
+ # Lib imports
+
+ # Application imports
+
+
+ def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+
+ return wrapper
+
+
+ class CrossClassSignals:
+ def __init__(self, settings):
+ self.settings = settings
+ self.builder = self.settings.returnBuilder()
+
+
+ def getClipboardData(self):
+ proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
+ retcode = proc.wait()
+ data = proc.stdout.read()
+ return data.decode("utf-8").strip()
+
+ def setClipboardData(self, data):
+ proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
+ proc.stdin.write(data)
+ proc.stdin.close()
+ retcode = proc.wait()
+
+ """
+
+
+ 'Python Glade Generic Template':
+ 'prefix': 'glade generic class'
+ 'body': """# Python imports
+
+ # Lib imports
+
+ # Application imports
+
+
+ class GenericClass:
+ def __init__(self):
+ super(GenericClass, self).__init__()
+
+ self._setup_styling()
+ self._setup_signals()
+ self._subscribe_to_events()
+ self._load_widgets()
+
+
+ def _setup_styling(self):
+ ...
+
+ def _setup_signals(self):
+ ...
+
+ def _subscribe_to_events(self):
+ event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
+
+ def _load_widgets(self):
+ ...
+
+ """
diff --git a/src/core/controllers/base_controller.py b/src/core/controllers/base_controller.py
index 3aeb5cb..c95c9da 100644
--- a/src/core/controllers/base_controller.py
+++ b/src/core/controllers/base_controller.py
@@ -24,11 +24,14 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerMixin)
def __init__(self):
self._setup_controller_data()
+
+ self._load_plugins(is_pre = True)
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_controllers()
- self._load_plugins_and_files()
+ self._load_plugins(is_pre = False)
+ self._load_files()
logger.info(f"Made it past {self.__class__} loading...")
settings_manager.set_end_load_time()
@@ -62,13 +65,19 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerMixin)
def _load_controllers(self):
BridgeController()
- def _load_plugins_and_files(self):
+ def _load_plugins(self, is_pre: bool):
args, unknownargs = settings_manager.get_starting_args()
+ if args.no_plugins == "true": return
- if args.no_plugins == "false":
+ if is_pre:
self.plugins_controller.pre_launch_plugins()
- self.plugins_controller.post_launch_plugins()
+ return
+ if not is_pre:
+ self.plugins_controller.post_launch_plugins()
+ return
+
+ def _load_files(self):
for file in settings_manager.get_starting_files():
event_system.emit("post-file-to-ipc", file)
diff --git a/src/core/widgets/code/code_base.py b/src/core/widgets/code/code_base.py
index 1b5dbb0..8013c08 100644
--- a/src/core/widgets/code/code_base.py
+++ b/src/core/widgets/code/code_base.py
@@ -11,7 +11,7 @@ from .controllers.files_controller import FilesController
from .controllers.tabs_controller import TabsController
from .controllers.commands_controller import CommandsController
from .controllers.completion_controller import CompletionController
-from .controllers.source_views_controller import SourceViewsController
+from .controllers.views.source_views_controller import SourceViewsController
from .mini_view_widget import MiniViewWidget
@@ -50,7 +50,12 @@ class CodeBase:
return self.miniview_widget
def create_source_view(self):
- return self.controller_manager["source_views"].create_source_view()
+ source_view = self.controller_manager["source_views"].create_source_view()
+ self.controller_manager["completion"].register_completer(
+ source_view.get_completion()
+ )
+
+ return source_view
def first_map_load(self):
self.controller_manager["source_views"].first_map_load()
diff --git a/src/core/widgets/code/command_system/command_system.py b/src/core/widgets/code/command_system/command_system.py
index 63986f2..48af7ed 100644
--- a/src/core/widgets/code/command_system/command_system.py
+++ b/src/core/widgets/code/command_system/command_system.py
@@ -39,11 +39,11 @@ class CommandSystem:
def emit(self, event: Code_Event_Types.CodeEvent):
- """ Monky patch 'emit' from command controller... """
+ """ Monkey patch 'emit' from command controller... """
...
def emit_to(self, controller: str, event: Code_Event_Types.CodeEvent):
- """ Monky patch 'emit' from command controller... """
+ """ Monkey patch 'emit_to' from command controller... """
...
diff --git a/src/core/widgets/code/command_system/commands/show_completion.py b/src/core/widgets/code/command_system/commands/show_completion.py
index dedb491..baec540 100644
--- a/src/core/widgets/code/command_system/commands/show_completion.py
+++ b/src/core/widgets/code/command_system/commands/show_completion.py
@@ -15,4 +15,14 @@ def execute(
view: GtkSource.View = None
):
logger.debug("Command: Show Completion")
- view.command.request_completion(view)
+ completer = view.get_completion()
+ providers = completer.get_providers()
+
+ if not providers:
+ view.command.request_completion(view)
+ return
+
+ completer.start(
+ providers,
+ completer.create_context()
+ )
diff --git a/src/core/widgets/code/completion_providers/example_completion_provider.py b/src/core/widgets/code/completion_providers/example_completion_provider.py
deleted file mode 100644
index 10f29a0..0000000
--- a/src/core/widgets/code/completion_providers/example_completion_provider.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Python imports
-import re
-
-# Lib imports
-import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GtkSource', '4')
-
-from gi.repository import Gtk
-from gi.repository import GtkSource
-from gi.repository import GObject
-
-# Application imports
-
-
-
-class ExampleCompletionProvider(GObject.GObject, GtkSource.CompletionProvider):
- """
- This is a custom Completion Example Provider.
- # NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
- """
- __gtype_name__ = 'ExampleCompletionProvider'
-
- def __init__(self):
- GObject.Object.__init__(self)
-
- def do_get_name(self):
- """ Returns: a new string containing the name of the provider. """
- return 'Example Completion Provider'
-
- def do_match(self, context):
- """ Get whether the provider match the context of completion detailed in context. """
- # NOTE: True for debugging but context needs to normally get checked for actual usage needs.
- # TODO: Fix me
- return True
-
- def do_get_priority(self):
- """ Determin position in result list along other providor results. """
- return 1
-
- # def do_get_activation(self):
- # """ The context for when a provider will show results """
- # return GtkSource.CompletionActivation.NONE
- # return GtkSource.CompletionActivation.USER_REQUESTED
- # return GtkSource.CompletionActivation.INTERACTIVE
-
- def do_populate(self, context):
- """
- In this instance, it will do 2 things:
- 1) always provide Hello World! (Not ideal but an option so its in the example)
- 2) Utilizes the Gtk.TextIter from the TextBuffer to determine if there is a jinja
- example of '{{ custom.' if so it will provide you with the options of foo and bar.
- If selected it will insert foo }} or bar }}, completing your syntax...
-
- PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
- """
-
- proposals = [
- GtkSource.CompletionItem(label='Hello World!', text = 'Hello World!', icon = None, info = None) # NOTE: Always proposed...
- ]
-
- # Gtk Versions differ on get_iter responses...
- end_iter = context.get_iter()
- if not isinstance(end_iter, Gtk.TextIter):
- _, end_iter = context.get_iter()
-
- if end_iter:
- buf = end_iter.get_buffer()
- mov_iter = end_iter.copy()
- if mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY):
- mov_iter, _ = mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY)
- left_text = buf.get_text(mov_iter, end_iter, True)
- else:
- left_text = ''
-
- if re.match(r'.*\{\{\s*custom\.$', left_text):
- proposals.append(
- GtkSource.CompletionItem(label='foo', text='foo }}') # optionally proposed based on left search via regex
- )
- proposals.append(
- GtkSource.CompletionItem(label='bar', text='bar }}') # optionally proposed based on left search via regex
- )
-
- context.add_proposals(self, proposals, True)
\ No newline at end of file
diff --git a/src/core/widgets/code/completion_providers/lsp_completion_provider.py b/src/core/widgets/code/completion_providers/lsp_completion_provider.py
deleted file mode 100644
index 45b76ba..0000000
--- a/src/core/widgets/code/completion_providers/lsp_completion_provider.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# Python imports
-
-# Lib imports
-import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GtkSource', '4')
-
-from gi.repository import Gtk
-from gi.repository import GtkSource
-from gi.repository import GObject
-
-# Application imports
-
-
-
-class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
- """
- This code is an LSP code completion plugin for Newton.
- # NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
- """
- __gtype_name__ = 'LSPProvider'
-
- def __init__(self):
- GObject.Object.__init__(self)
-
- self._icon_theme = Gtk.IconTheme.get_default()
-
- self.lsp_data = None
-
-
- def pre_populate(self, context):
- ...
-
- def do_get_name(self):
- return "LSP Code Completion"
-
- def get_iter_correctly(self, context):
- return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
-
- def do_match(self, context):
- iter = self.get_iter_correctly(context)
- iter.backward_char()
-
- buffer = iter.get_buffer()
- if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
- return False
-
- ch = iter.get_char()
- # NOTE: Look to re-add or apply supprting logic to use spaces
- # As is it slows down the editor in certain contexts...
- # if not (ch in ('_', '.', ' ') or ch.isalnum()):
- if not (ch in ('_', '.') or ch.isalnum()):
- return False
-
- return True
-
- def do_get_priority(self):
- return 5
-
- def do_populate(self, context, items = []):
- # self.lsp_data
- proposals = []
-
- comp_item = GtkSource.CompletionItem.new()
- comp_item.set_label("LSP Class")
- comp_item.set_text("LSP Code")
- # comp_item.set_icon(self.get_icon_for_type(completion.type))
- comp_item.set_info("A test LSP completion item...")
-
- context.add_proposals(self, [comp_item], True)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- # def do_populate(self, context, items = []):
- # if hasattr(self._source_view, "completion_items"):
- # items = self._source_view.completion_items
-
- # proposals = []
- # for item in items:
- # proposals.append( self.create_completion_item(item) )
-
- # context.add_proposals(self, proposals, True)
-
- # def get_icon_for_type(self, _type):
- # try:
- # return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
- # except:
- # ...
-
- # try:
- # return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
- # except:
- # ...
-
- # return None
-
- # def create_completion_item(self, item):
- # comp_item = GtkSource.CompletionItem.new()
- # keys = item.keys()
- # comp_item.set_label(item["label"])
-
- # if "insertText" in keys:
- # comp_item.set_text(item["insertText"])
-
- # if "additionalTextEdits" in keys:
- # comp_item.additionalTextEdits = item["additionalTextEdits"]
-
- # return comp_item
-
-
- # def create_completion_item(self, item):
- # comp_item = GtkSource.CompletionItem.new()
- # comp_item.set_label(item.label)
-
- # if item.textEdit:
- # if isinstance(item.textEdit, dict):
- # comp_item.set_text(item.textEdit["newText"])
- # else:
- # comp_item.set_text(item.textEdit)
- # else:
- # comp_item.set_text(item.insertText)
-
- # comp_item.set_icon( self.get_icon_for_type(item.kind) )
- # comp_item.set_info(item.documentation)
-
- # return comp_item
\ No newline at end of file
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
new file mode 100644
index 0000000..0ecce4d
--- /dev/null
+++ b/src/core/widgets/code/completion_providers/provider_response_cache_base.py
@@ -0,0 +1,87 @@
+# Python imports
+import re
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import GtkSource
+
+# Application imports
+
+
+
+class ProviderResponseCacheException(Exception):
+ ...
+
+
+
+class ProviderResponseCacheBase:
+ def __init__(self):
+ super(ProviderResponseCacheBase, self).__init__()
+
+ self._icon_theme = Gtk.IconTheme.get_default()
+
+
+ def process_file_load(self, buffer: GtkSource.Buffer):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_load' not implemented...")
+
+ def process_file_close(self, buffer: GtkSource.Buffer):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_close' not implemented...")
+
+ def process_file_save(self, buffer: GtkSource.Buffer):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_file_save' not implemented...")
+
+ def process_file_change(self, buffer: GtkSource.Buffer):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...")
+
+ def filter(self, word: str):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter' not implemented...")
+
+ def filter_with_context(self, context: GtkSource.CompletionContext):
+ raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
+
+
+ def create_completion_item(
+ self,
+ label: str = "",
+ text: str = "",
+ info: str = "",
+ completion: any = None
+ ):
+ if not label or not text: return
+
+ comp_item = GtkSource.CompletionItem.new()
+ comp_item.set_label(label)
+ comp_item.set_text(text)
+
+ if info:
+ comp_item.set_info(info)
+ # comp_item.set_markup(f"{info}
")
+
+ if completion:
+ comp_item.set_icon(
+ self.get_icon_for_type(completion.type)
+ )
+
+ return comp_item
+
+ def get_word(self, context):
+ start_iter = context.get_iter()
+ end_iter = None
+
+ if not isinstance(start_iter, Gtk.TextIter):
+ _, start_iter = context.get_iter()
+ end_iter = start_iter.copy()
+
+ if not start_iter.starts_word():
+ start_iter.backward_word_start()
+
+ end_iter.forward_word_end()
+
+ buffer = start_iter.get_buffer()
+
+ return buffer.get_text(start_iter, end_iter, False)
diff --git a/src/core/widgets/code/completion_providers/python_completion_provider.py b/src/core/widgets/code/completion_providers/python_completion_provider.py
deleted file mode 100644
index 319ce99..0000000
--- a/src/core/widgets/code/completion_providers/python_completion_provider.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Python imports
-
-# Lib imports
-import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GtkSource', '4')
-
-from gi.repository import Gtk
-from gi.repository import GtkSource
-from gi.repository import GObject
-
-import jedi
-from jedi.api import Script
-
-# Application imports
-
-
-
-# FIXME: Find real icon names...
-icon_names = {
- 'import': '',
- 'module': '',
- 'class': '',
- 'function': '',
- 'statement': '',
- 'param': ''
-}
-
-
-class Jedi:
- def get_script(file, doc_text):
- return Script(code = doc_text, path = file)
-
-
-class PythonCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
- """
- This code is A python code completion plugin for Newton.
- # NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
- """
- __gtype_name__ = 'PythonProvider'
-
- def __init__(self, file):
- GObject.Object.__init__(self)
- self._theme = Gtk.IconTheme.get_default()
- self._file = file
-
- def do_get_name(self):
- return "Python Code Completion"
-
- def get_iter_correctly(self, context):
- return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
-
- def do_match(self, context):
- iter = self.get_iter_correctly(context)
- iter.backward_char()
-
- buffer = iter.get_buffer()
- if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
- return False
-
- ch = iter.get_char()
- # NOTE: Look to re-add or apply supprting logic to use spaces
- # As is it slows down the editor in certain contexts...
- # if not (ch in ('_', '.', ' ') or ch.isalnum()):
- if not (ch in ('_', '.') or ch.isalnum()):
- return False
-
- return True
-
- def do_get_priority(self):
- return 1
-
- def do_get_activation(self):
- return GtkSource.CompletionActivation.INTERACTIVE
-
- def do_populate(self, context):
- # TODO: Maybe convert async?
- it = self.get_iter_correctly(context)
- buffer = it.get_buffer()
- proposals = []
-
- doc_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
- iter_cursor = buffer.get_iter_at_mark(buffer.get_insert())
- linenum = iter_cursor.get_line() + 1
- charnum = iter_cursor.get_line_index()
-
- def create_generator():
- for completion in Jedi.get_script(self._file, doc_text).complete(line = linenum, column = None, fuzzy = False):
- comp_item = GtkSource.CompletionItem.new()
- comp_item.set_label(completion.name)
- comp_item.set_text(completion.name)
- comp_item.set_icon(self.get_icon_for_type(completion.type))
- comp_item.set_info(completion.docstring())
- yield comp_item
-
- for item in create_generator():
- proposals.append(item)
-
- context.add_proposals(self, proposals, True)
-
- def get_icon_for_type(self, _type):
- try:
- return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
- except (KeyError, AttributeError, GObject.GError) as e:
- return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
- except (GObject.GError, AttributeError) as e:
- return None
\ No newline at end of file
diff --git a/src/core/widgets/code/controllers/__init__.py b/src/core/widgets/code/controllers/__init__.py
index e614b70..8f3b747 100644
--- a/src/core/widgets/code/controllers/__init__.py
+++ b/src/core/widgets/code/controllers/__init__.py
@@ -1,3 +1,3 @@
"""
Code Controllers Package
-"""
\ No newline at end of file
+"""
diff --git a/src/core/widgets/code/controllers/completion_controller.py b/src/core/widgets/code/controllers/completion_controller.py
index 6330841..91dec63 100644
--- a/src/core/widgets/code/controllers/completion_controller.py
+++ b/src/core/widgets/code/controllers/completion_controller.py
@@ -11,93 +11,84 @@ from gi.repository import GtkSource
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
-from ..completion_providers.example_completion_provider import ExampleCompletionProvider
-from ..completion_providers.lsp_completion_provider import LSPCompletionProvider
-
class CompletionController(ControllerBase):
def __init__(self):
super(CompletionController, self).__init__()
- self._completor: GtkSource.Completion = None
- self._timeout_id: int = None
- self._lsp_provider: LSPCompletionProvider = LSPCompletionProvider()
+ 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] = {}
def _controller_message(self, event: Code_Event_Types.CodeEvent):
- if isinstance(event, Code_Event_Types.FocusedViewEvent):
- self._completor = event.view.get_completion()
-
- if not self._timeout_id: return
-
- GLib.source_remove(self._timeout_id)
- self._timeout_id = None
- elif isinstance(event, Code_Event_Types.RequestCompletionEvent):
- self.request_completion()
- # elif isinstance(event, Code_Event_Types.TextInsertedEvent):
- # self.request_completion()
-
- def _process_request_completion(self):
- self._start_completion()
-
- self._timeout_id = None
- return False
-
- def _do_completion(self):
- if self._completor.get_providers():
- self._match_completion()
- else:
- self._start_completion()
-
- def _match_completion(self):
- """
- Note: Use IF providers were added to completion...
- """
- self._completion.match(
- self._completion.create_context()
- )
+ if isinstance(event, Code_Event_Types.RegisterProviderEvent):
+ self.register_provider(
+ event.provider_name,
+ event.provider,
+ event.language_ids
+ )
+ elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
+ self.provider_process_file_load(event)
+ elif isinstance(event, Code_Event_Types.RemovedFileEvent):
+ self.provider_process_file_close(event)
+ elif isinstance(event, Code_Event_Types.SavedFileEvent):
+ self.provider_process_file_save(event)
+ elif isinstance(event, Code_Event_Types.TextChangedEvent):
+ self.provider_process_file_change(event)
+ # elif isinstance(event, Code_Event_Types.RequestCompletionEvent):
+ # self.request_unbound_completion( event.view.get_completion() )
- def _start_completion(self):
- """
- Note: Use IF NO providers have been added to completion...
- """
- self._completor.start(
- [
- ExampleCompletionProvider(),
- self._lsp_provider
- ],
- self._completor.create_context()
- )
+ def register_completer(self, completer: GtkSource.Completion):
+ self._completers.append(completer)
-
- def set_completer(self, completer):
- self._completor = completer
-
- def request_completion(self):
- if self._timeout_id:
- GLib.source_remove(self._timeout_id)
-
- self._timeout_id = GLib.timeout_add(
- 800,
- self._process_request_completion
- )
+ completer.add_provider(self.words_provider)
+ for provider in self._providers.values():
+ completer.add_provider(provider)
def register_provider(
self,
provider_name: str,
- provider: GtkSource.CompletionProvider,
- priority: int = 0,
- language_ids: list = None
+ provider: GtkSource.CompletionProvider,
+ language_ids: list = []
):
- """Register completion providers with priority and language filtering"""
- ...
+ self._providers[provider_name] = provider
+
+ for completer in self._completers:
+ completer.add_provider(provider)
def unregister_provider(self, provider_name: str):
- """Remove completion providers"""
- ...
-
- def get_active_providers(self, language_id: str = None) -> list:
- """Get providers filtered by language"""
- ...
+ provider = self._providers[provider_name]
+ del self._providers[provider_name]
+
+ for completer in self._completers:
+ 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)
+
+ def provider_process_file_save(self, event: Code_Event_Types.SavedFileEvent):
+ for provider in self._providers.values():
+ provider.response_cache.process_file_save(event)
+
+ def provider_process_file_change(self, event: Code_Event_Types.TextChangedEvent):
+ for provider in self._providers.values():
+ provider.response_cache.process_file_change(event)
+
+ def request_unbound_completion(self, completer: GtkSource.Completion):
+ completer.start(
+ [ *self._providers.values() ],
+ completer.create_context()
+ )
diff --git a/src/core/widgets/code/controllers/source_views_controller.py b/src/core/widgets/code/controllers/source_views_controller.py
deleted file mode 100644
index c2ff44d..0000000
--- a/src/core/widgets/code/controllers/source_views_controller.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# Python imports
-
-# Lib imports
-
-# Application imports
-from libs.controllers.controller_base import ControllerBase
-from libs.event_factory import Event_Factory, Code_Event_Types
-
-from ..command_system import CommandSystem
-from ..key_mapper import KeyMapper
-
-from ..source_view import SourceView
-
-
-
-class SourceViewsController(ControllerBase, list):
- def __init__(self):
- super(SourceViewsController, self).__init__()
-
- self.key_mapper: KeyMapper = KeyMapper()
- self.active_view: SourceView = None
-
-
- def get_command_system(self):
- event = Event_Factory.create_event("get_command_system")
- self.message_to("commands", event)
- command = event.response
-
- del event
- return command
-
- def create_source_view(self):
- source_view: SourceView = SourceView()
- source_view.command = self.get_command_system()
- source_view.command.set_data(source_view)
-
- self._map_signals(source_view)
-
- self.append(source_view)
- return source_view
-
- def _controller_message(self, event: Code_Event_Types.CodeEvent):
- if isinstance(event, Code_Event_Types.RemovedFileEvent):
- self._remove_file(event)
- elif isinstance(event, Code_Event_Types.TextChangedEvent):
- self.active_view.command.exec("update_info_bar")
-
- def _map_signals(self, source_view: SourceView):
- source_view.connect("focus-in-event", self._focus_in_event)
- source_view.connect("move-cursor", self._move_cursor)
- source_view.connect("key-press-event", self._key_press_event)
- source_view.connect("key-release-event", self._key_release_event)
- source_view.connect("button-press-event", self._button_press_event)
- source_view.connect("button-release-event", self._button_release_event)
-
- def _focus_in_event(self, view, eve):
- self.active_view = view
-
- view.command.exec("set_miniview")
- view.command.exec("set_focus_border")
- view.command.exec("update_info_bar")
-
- event = Event_Factory.create_event("focused_view", view = view)
- self.emit(event)
-
- def _move_cursor(self, view, step, count, extend_selection):
- buffer = view.get_buffer()
- iter = buffer.get_iter_at_mark( buffer.get_insert() )
- line = iter.get_line()
- char = iter.get_line_offset()
-
- event = Event_Factory.create_event(
- "cursor_moved",
- view = view,
- buffer = buffer,
- line = line,
- char = char
- )
-
- self.emit(event)
-
- view.command.exec("update_info_bar")
-
- def _button_press_event(self, view, eve):
- self.active_view.command.exec("update_info_bar")
-
- def _button_release_event(self, view, eve):
- self.active_view.command.exec("update_info_bar")
-
- def _key_press_event(self, view, eve):
- command = self.key_mapper._key_press_event(eve)
- is_future = self.key_mapper._key_release_event(eve)
-
- if is_future: return True
- if not command: return False
-
- view.command.exec(command)
-
- return True
-
- def _key_release_event(self, view, eve):
- command = self.key_mapper._key_release_event(eve)
- is_past = self.key_mapper._key_press_event(eve)
-
- if is_past: return True
- if not command: return False
-
- view.command.exec(command)
-
- return True
-
- def _remove_file(self, event: Code_Event_Types.RemovedFileEvent):
- for view in self:
- if not event.file.buffer == view.get_buffer(): continue
- if not event.next_file:
- view.command.exec("new_file")
- continue
-
- view.set_buffer(event.next_file.buffer)
-
- def first_map_load(self):
- for view in self:
- view.command.exec("new_file")
-
- view = self[0]
- view.grab_focus()
- view.command.exec("load_start_files")
-
diff --git a/src/core/widgets/code/controllers/views/__init__.py b/src/core/widgets/code/controllers/views/__init__.py
new file mode 100644
index 0000000..1461e87
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/__init__.py
@@ -0,0 +1,10 @@
+"""
+ Code Controllers Package
+"""
+
+from .state_manager import SourceViewStateManager
+from .signal_mapper import SourceViewSignalMapper
+from .source_views_controller import SourceViewsController
+
+# State imports
+from .states import *
\ No newline at end of file
diff --git a/src/core/widgets/code/controllers/views/signal_mapper.py b/src/core/widgets/code/controllers/views/signal_mapper.py
new file mode 100644
index 0000000..8577f57
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/signal_mapper.py
@@ -0,0 +1,63 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from ...source_view import SourceView
+
+
+
+class SourceViewSignalMapper:
+ def __init__(self):
+ self.active_view: SourceView = None
+
+
+ def bind_emit(self, emit: callable):
+ self.emit = emit
+
+ def set_state_manager(self, state_manager):
+ self.state_manager = state_manager
+
+ def connect_signals(self, source_view: SourceView):
+ signal_mappings = self._get_signal_mappings()
+ for signal, handler in signal_mappings.items():
+ source_view.connect(signal, handler)
+
+ def disconnect_signals(self, source_view: SourceView):
+ signal_mappings = self._get_signal_mappings()
+ for signal, handler in signal_mappings.items():
+ source_view.disconnect_by_func(handler)
+
+ def insert_text(self, file, string: str):
+ return self.state_manager.handle_insert_text(self.active_view, file, string)
+
+ def _get_signal_mappings(self):
+ return {
+ "focus-in-event": self._focus_in_event,
+ "move-cursor": self._move_cursor,
+ "key-press-event": self._key_press_event,
+ "key-release-event": self._key_release_event,
+ "button-press-event": self._button_press_event,
+ "button-release-event": self._button_release_event
+ }
+
+ def _focus_in_event(self, source_view: SourceView, eve):
+ self.active_view = source_view
+ return self.state_manager.handle_focus_in_event(source_view, eve, self.emit)
+
+ def _move_cursor(self, source_view: SourceView, step, count, extend_selection):
+ return self.state_manager.handle_move_cursor(
+ source_view, step, count, extend_selection, self.emit
+ )
+
+ def _button_press_event(self, source_view: SourceView, eve):
+ return self.state_manager.handle_button_press_event(source_view, eve)
+
+ def _button_release_event(self, source_view: SourceView, eve):
+ return self.state_manager.handle_button_release_event(source_view, eve)
+
+ def _key_press_event(self, source_view: SourceView, eve):
+ return self.state_manager.handle_key_press_event(source_view, eve)
+
+ def _key_release_event(self, source_view: SourceView, eve):
+ return self.state_manager.handle_key_release_event(source_view, eve)
diff --git a/src/core/widgets/code/controllers/views/source_views_controller.py b/src/core/widgets/code/controllers/views/source_views_controller.py
new file mode 100644
index 0000000..08c914e
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/source_views_controller.py
@@ -0,0 +1,74 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.controllers.controller_base import ControllerBase
+from libs.event_factory import Event_Factory, Code_Event_Types
+
+from ...source_view import SourceView
+
+from .state_manager import SourceViewStateManager
+from .signal_mapper import SourceViewSignalMapper
+
+
+
+class SourceViewsController(ControllerBase, list):
+ def __init__(self):
+ super(SourceViewsController, self).__init__()
+
+ self.state_manager: SourceViewStateManager = SourceViewStateManager()
+ self.signal_mapper: SourceViewSignalMapper = SourceViewSignalMapper()
+
+ self.signal_mapper.bind_emit(self.emit)
+ self.signal_mapper.set_state_manager(self.state_manager)
+
+
+ def _controller_message(self, event: Code_Event_Types.CodeEvent):
+ if isinstance(event, Code_Event_Types.RemovedFileEvent):
+ self._remove_file(event)
+
+ if not self.signal_mapper.active_view: return
+
+ 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.TextInsertedEvent):
+ self.signal_mapper.insert_text(event.file, event.text)
+
+ def _get_command_system(self):
+ event = Event_Factory.create_event("get_command_system")
+ self.message_to("commands", event)
+ command = event.response
+
+ del event
+ return command
+
+ def _remove_file(self, event: Code_Event_Types.RemovedFileEvent):
+ for source_view in self:
+ if not event.file.buffer == source_view.get_buffer(): continue
+ if not event.next_file:
+ source_view.command.exec("new_file")
+ continue
+
+ source_view.set_buffer(event.next_file.buffer)
+
+ def create_source_view(self):
+ source_view: SourceView = SourceView()
+ source_view.command = self._get_command_system()
+ source_view.command.set_data(source_view)
+
+ self.signal_mapper.connect_signals(source_view)
+
+ self.append(source_view)
+ return source_view
+
+ def first_map_load(self):
+ for source_view in self:
+ source_view.command.exec("new_file")
+
+ source_view = self[0]
+ source_view.grab_focus()
+ source_view.command.exec("load_start_files")
diff --git a/src/core/widgets/code/controllers/views/state_manager.py b/src/core/widgets/code/controllers/views/state_manager.py
new file mode 100644
index 0000000..8bae1a1
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/state_manager.py
@@ -0,0 +1,65 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.dto.states import SourceViewStates
+
+from ...key_mapper import KeyMapper
+
+from .states import *
+
+
+class SourceViewStateManager:
+ def __init__(self):
+ self.key_mapper: KeyMapper = KeyMapper()
+
+ self.states: dict = {
+ SourceViewStates.INSERT: SourceViewsInsertState(),
+ SourceViewStates.MULTIINSERT: SourceViewsMultiInsertState(),
+ SourceViewStates.COMMAND: SourceViewsCommandState(),
+ SourceViewStates.READONLY: SourceViewsReadOnlyState()
+ }
+
+
+ def handle_focus_in_event(self, source_view, eve, emit):
+ return self.states[source_view.state].focus_in_event(source_view, eve, emit)
+
+ def handle_insert_text(self, source_view, file, text):
+ return self.states[source_view.state].insert_text(file, text)
+
+ def handle_move_cursor(self, source_view, step, count, extend_selection, emit):
+ return self.states[source_view.state].move_cursor(
+ source_view, step, count, extend_selection, emit
+ )
+
+ def handle_button_press_event(self, source_view, eve):
+ return self.states[source_view.state].button_press_event(source_view, eve)
+
+ def handle_button_release_event(self, source_view, eve):
+ # Handle state transitions (multi-insert toggling)
+ self._handle_multi_insert_toggle(source_view, eve)
+
+ return self.states[source_view.state].button_release_event(source_view, eve)
+
+ def handle_key_press_event(self, source_view, eve):
+ return self.states[source_view.state].key_press_event(
+ source_view, eve, self.key_mapper
+ )
+
+ def handle_key_release_event(self, source_view, eve):
+ return self.states[source_view.state].key_release_event(
+ source_view, eve, self.key_mapper
+ )
+
+ def _handle_multi_insert_toggle(self, source_view, eve):
+ is_control = self.key_mapper.is_control(eve)
+ if is_control and not source_view.state == SourceViewStates.MULTIINSERT:
+ logger.debug("Entered Multi-Insert Mode...")
+ source_view.state = SourceViewStates.MULTIINSERT
+
+ if not is_control and source_view.state == SourceViewStates.MULTIINSERT:
+ logger.debug("Entered Regular Insert Mode...")
+ self.states[source_view.state].clear_markers(source_view)
+
+ source_view.state = SourceViewStates.INSERT
diff --git a/src/core/widgets/code/controllers/views/states/__init__.py b/src/core/widgets/code/controllers/views/states/__init__.py
new file mode 100644
index 0000000..9b79f5c
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/states/__init__.py
@@ -0,0 +1,8 @@
+"""
+ Code Controllers Views States Package
+"""
+
+from .source_view_insert_state import SourceViewsInsertState
+from .source_view_multi_insert_state import SourceViewsMultiInsertState
+from .source_view_command_state import SourceViewsCommandState
+from .source_view_read_only_state import SourceViewsReadOnlyState
diff --git a/src/core/widgets/code/controllers/views/states/source_view_command_state.py b/src/core/widgets/code/controllers/views/states/source_view_command_state.py
new file mode 100644
index 0000000..d57d808
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/states/source_view_command_state.py
@@ -0,0 +1,36 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.event_factory import Event_Factory, Code_Event_Types
+
+from libs.dto.states import SourceViewStates
+
+
+
+class SourceViewsCommandState:
+ def __init__(self):
+ super(SourceViewsCommandState, self).__init__()
+
+
+ def focus_in_event(self, source_view, eve, emit):
+ return True
+
+ def move_cursor(self, source_view, step, count, extend_selection, emit):
+ return True
+
+ def insert_text(self, file, text):
+ return True
+
+ def button_press_event(self, source_view, eve):
+ return True
+
+ def button_release_event(self, source_view, eve):
+ return True
+
+ def key_press_event(self, source_view, eve, key_mapper):
+ return True
+
+ def key_release_event(self, source_view, eve, key_mapper):
+ return True
diff --git a/src/core/widgets/code/controllers/views/states/source_view_insert_state.py b/src/core/widgets/code/controllers/views/states/source_view_insert_state.py
new file mode 100644
index 0000000..7a2bd66
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/states/source_view_insert_state.py
@@ -0,0 +1,71 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.event_factory import Event_Factory, Code_Event_Types
+
+from libs.dto.states import SourceViewStates
+
+
+
+class SourceViewsInsertState:
+ def __init__(self):
+ super(SourceViewsInsertState, self).__init__()
+
+
+ def focus_in_event(self, source_view, eve, emit):
+ source_view.command.exec("set_miniview")
+ source_view.command.exec("set_focus_border")
+ source_view.command.exec("update_info_bar")
+
+ event = Event_Factory.create_event("focused_view", view = source_view)
+ emit(event)
+
+ def insert_text(self, file, text):
+ return True
+
+ def move_cursor(self, source_view, step, count, extend_selection, emit):
+ buffer = source_view.get_buffer()
+ itr = buffer.get_iter_at_mark( buffer.get_insert() )
+ line = itr.get_line()
+ char = itr.get_line_offset()
+ event = Event_Factory.create_event(
+ "cursor_moved",
+ view = source_view,
+ buffer = buffer,
+ line = line,
+ char = char
+ )
+
+ emit(event)
+
+ source_view.command.exec("update_info_bar")
+
+ def button_press_event(self, source_view, eve):
+ source_view.command.exec("update_info_bar")
+
+ def button_release_event(self, source_view, eve):
+ source_view.command.exec("update_info_bar")
+
+ def key_press_event(self, source_view, eve, key_mapper):
+ command = key_mapper._key_press_event(eve)
+ is_future = key_mapper._key_release_event(eve)
+
+ if is_future: return True
+ if not command: return False
+
+ source_view.command.exec(command)
+
+ return True
+
+ def key_release_event(self, source_view, eve, key_mapper):
+ command = key_mapper._key_release_event(eve)
+ is_past = key_mapper._key_press_event(eve)
+
+ if is_past: return True
+ if not command: return False
+
+ source_view.command.exec(command)
+
+ return True
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
new file mode 100644
index 0000000..8de34be
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/states/source_view_multi_insert_state.py
@@ -0,0 +1,131 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from libs.event_factory import Event_Factory, Code_Event_Types
+from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
+
+from ....mixins.source_mark_events_mixin import MarkEventsMixin
+
+
+
+class SourceViewsMultiInsertState(MarkEventsMixin):
+ def __init__(self):
+ super(SourceViewsMultiInsertState, self).__init__()
+
+ self.cursor_action: CursorAction = 0
+ self.move_direction: MoveDirection = 0
+ self.insert_markers: list = []
+
+
+ def focus_in_event(self, source_view, eve, emit):
+ source_view.command.exec("set_miniview")
+ source_view.command.exec("set_focus_border")
+ source_view.command.exec("update_info_bar")
+
+ event = Event_Factory.create_event("focused_view", view = source_view)
+ emit(event)
+
+ def insert_text(self, file, text):
+ if not self.insert_markers: return False
+
+ buffer = file.buffer
+
+ # freeze buffer and insert to each mark (if any)
+ buffer.block_insert_after_signal()
+ buffer.begin_user_action()
+
+ with buffer.freeze_notify():
+ for mark in self.insert_markers:
+ itr = buffer.get_iter_at_mark(mark)
+ buffer.insert(itr, text, -1)
+
+ buffer.end_user_action()
+ buffer.unblock_insert_after_signal()
+
+ return True
+
+ def move_cursor(self, source_view, step, count, extend_selection, emit):
+ buffer = source_view.get_buffer()
+
+ self._process_move_direction(buffer)
+ self._signal_cursor_moved(source_view, emit)
+ source_view.command.exec("update_info_bar")
+
+ def button_press_event(self, source_view, eve):
+ source_view.command.exec("update_info_bar")
+ return True
+
+ def button_release_event(self, source_view, eve):
+ buffer = source_view.get_buffer()
+ insert_iter = buffer.get_iter_at_mark( buffer.get_insert() )
+ data = source_view.window_to_buffer_coords(
+ Gtk.TextWindowType.TEXT,
+ eve.x,
+ eve.y
+ )
+ is_over_text, \
+ target_iter, \
+ is_trailing = source_view.get_iter_at_position(data.buffer_x, data.buffer_y)
+
+ if not is_over_text:
+ # NOTE: Trying to put at very end of line if not over text (aka, clicking right of text)
+ target_iter.forward_visible_line()
+ target_iter.backward_char()
+
+ self._insert_mark(insert_iter, target_iter, buffer)
+
+ def key_press_event(self, source_view, eve, key_mapper):
+ char = key_mapper.get_raw_keyname(eve)
+
+ for action in CursorAction:
+ if not action.name == char.upper(): continue
+ self.cursor_action = action.value
+ self._process_cursor_action(source_view.get_buffer())
+
+ return False
+
+ for direction in MoveDirection:
+ if not direction.name == char.upper(): continue
+ self.move_direction = direction.value
+ return False
+
+ is_future = key_mapper._key_release_event(eve)
+ if is_future: return False
+
+ command = key_mapper._key_press_event(eve)
+ if not command: return False
+
+ source_view.command.exec(command)
+
+ return True
+
+ def key_release_event(self, source_view, eve, key_mapper):
+ command = key_mapper._key_release_event(eve)
+ is_past = key_mapper._key_press_event(eve)
+
+ if is_past: return False
+ if not command: return False
+
+ source_view.command.exec(command)
+
+ return True
+
+ def _signal_cursor_moved(self, source_view, emit):
+ buffer = source_view.get_buffer()
+ itr = buffer.get_iter_at_mark( buffer.get_insert() )
+ line = itr.get_line()
+ char = itr.get_line_offset()
+ event = Event_Factory.create_event(
+ "cursor_moved",
+ view = source_view,
+ buffer = buffer,
+ line = line,
+ char = char
+ )
+
+ emit(event)
diff --git a/src/core/widgets/code/controllers/views/states/source_view_read_only_state.py b/src/core/widgets/code/controllers/views/states/source_view_read_only_state.py
new file mode 100644
index 0000000..07317d2
--- /dev/null
+++ b/src/core/widgets/code/controllers/views/states/source_view_read_only_state.py
@@ -0,0 +1,36 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.event_factory import Event_Factory, Code_Event_Types
+
+from libs.dto.states import SourceViewStates
+
+
+
+class SourceViewsReadOnlyState:
+ def __init__(self):
+ super(SourceViewsReadOnlyState, self).__init__()
+
+
+ def focus_in_event(self, source_view, eve, emit):
+ return True
+
+ def move_cursor(self, source_view, step, count, extend_selection, emit):
+ return True
+
+ def insert_text(self, file, text):
+ return True
+
+ def button_press_event(self, source_view, eve):
+ return True
+
+ def button_release_event(self, source_view, eve):
+ return True
+
+ def key_press_event(self, source_view, eve, key_mapper):
+ return True
+
+ def key_release_event(self, source_view, eve, key_mapper):
+ return True
diff --git a/src/core/widgets/code/key_mapper.py b/src/core/widgets/code/key_mapper.py
index 9ddfb96..9193401 100644
--- a/src/core/widgets/code/key_mapper.py
+++ b/src/core/widgets/code/key_mapper.py
@@ -125,3 +125,13 @@ class KeyMapper:
if is_alt:
self.state = self.state | AltKeyState
+ def is_control(self, eve):
+ modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
+ return True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
+
+ def is_shift(self, eve):
+ modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
+ return True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
+
+ def get_raw_keyname(self, eve):
+ return Gdk.keyval_name(eve.keyval)
diff --git a/src/core/widgets/code/mixins/source_mark_events_mixin.py b/src/core/widgets/code/mixins/source_mark_events_mixin.py
new file mode 100644
index 0000000..460d70c
--- /dev/null
+++ b/src/core/widgets/code/mixins/source_mark_events_mixin.py
@@ -0,0 +1,107 @@
+# Python imports
+import random
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
+
+
+
+class MarkEventsMixin:
+ def clear_markers(self, source_view):
+ buffer = source_view.get_buffer()
+
+ for mark in self.insert_markers:
+ mark.set_visible(False)
+ buffer.delete_mark(mark)
+
+ self.insert_markers.clear()
+
+ def _insert_mark(self, insert_iter, target_iter, buffer):
+ mark_found = self._check_for_insert_marks(target_iter, buffer)
+
+ if mark_found: return
+
+ random_bits = random.getrandbits(128)
+ hash = "%032x" % random_bits
+
+ mark = Gtk.TextMark.new(
+ name = f"multi_insert_{hash}",
+ left_gravity = False
+ )
+
+ buffer.add_mark(mark, target_iter)
+ mark.set_visible(True)
+ self.insert_markers.append(mark)
+
+
+ def _check_for_insert_marks(self, target_iter, buffer):
+ marks = target_iter.get_marks()
+
+ for mark in marks:
+ if mark in self.insert_markers[:]:
+ mark.set_visible(False)
+ self.insert_markers.remove(mark)
+ buffer.delete_mark(mark)
+ return True
+
+ insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
+ if target_iter.equal(insert_itr): return True
+
+ return False
+
+ def _process_cursor_action(self, buffer):
+ if not self.insert_markers: return
+ if self.cursor_action == CursorAction.NONE.value: return
+
+ action = self.cursor_action
+ for mark in self.insert_markers:
+ itr = buffer.get_iter_at_mark(mark)
+
+ if action == CursorAction.BACKSPACE.value:
+ buffer.backspace(itr, interactive = True, default_editable = True)
+ elif action == CursorAction.DELETE.value:
+ itr.forward_char()
+ buffer.backspace(itr, interactive = True, default_editable = True)
+ elif action == CursorAction.ENTER.value:
+ ...
+
+ self.cursor_action = CursorAction.NONE.value
+
+ def _process_move_direction(self, buffer):
+ if not self.insert_markers: return
+ if self.move_direction == MoveDirection.NONE.value: return
+
+ direction = self.move_direction
+ for mark in self.insert_markers:
+ itr = buffer.get_iter_at_mark(mark)
+
+ if direction == MoveDirection.UP.value:
+ new_line = itr.get_line() - 1
+ new_itr = buffer.get_iter_at_line_offset(
+ new_line,
+ itr.get_line_index()
+ )
+
+ elif direction == MoveDirection.DOWN.value:
+ new_line = itr.get_line() + 1
+ new_itr = buffer.get_iter_at_line_offset(
+ new_line,
+ itr.get_line_index()
+ )
+ elif direction == MoveDirection.LEFT.value:
+ if not itr.backward_char(): break
+ new_itr = itr
+ elif direction == MoveDirection.RIGHT.value:
+ if not itr.forward_char(): break
+ new_itr = itr
+ else:
+ continue
+
+ buffer.move_mark_by_name(mark.get_name(), new_itr)
+
+ self.move_direction = MoveDirection.NONE
diff --git a/src/core/widgets/code/source_buffer.py b/src/core/widgets/code/source_buffer.py
index 8c70296..083d15c 100644
--- a/src/core/widgets/code/source_buffer.py
+++ b/src/core/widgets/code/source_buffer.py
@@ -21,20 +21,41 @@ class SourceBuffer(GtkSource.Buffer):
_changed,
_mark_set,
_insert_text,
+ _after_insert_text,
_modified_changed,
):
self._handler_ids = [
- self.connect("changed", _changed),
- self.connect("mark-set", _mark_set),
- self.connect("insert-text", _insert_text),
- self.connect("modified-changed", _modified_changed)
+ self.connect("changed", _changed),
+ self.connect("mark-set", _mark_set),
+ self.connect("insert-text", _insert_text),
+ self.connect_after("insert-text", _after_insert_text),
+ self.connect("modified-changed", _modified_changed)
]
+ def block_changed_signal(self):
+ self.handler_block(self._handler_ids[0])
+
+ def block_insert_after_signal(self):
+ self.handler_block(self._handler_ids[3])
+
+ def block_modified_changed_signal(self):
+ self.handler_block(self._handler_ids[4])
+
+ def unblock_changed_signal(self):
+ self.handler_unblock(self._handler_ids[0])
+
+ def unblock_insert_after_signal(self):
+ self.handler_unblock(self._handler_ids[3])
+
+ def unblock_modified_changed_signal(self):
+ self.handler_unblock(self._handler_ids[4])
+
def clear_signals(self):
for handle_id in self._handler_ids:
self.disconnect(handle_id)
def __del__(self):
for handle_id in self._handler_ids:
- self.disconnect(handle_id)
\ No newline at end of file
+ self.disconnect(handle_id)
+
diff --git a/src/core/widgets/code/source_file.py b/src/core/widgets/code/source_file.py
index 02817c1..43288af 100644
--- a/src/core/widgets/code/source_file.py
+++ b/src/core/widgets/code/source_file.py
@@ -8,6 +8,7 @@ gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk
+from gi.repository import GLib
from gi.repository import GtkSource
from gi.repository import Gio
@@ -37,6 +38,7 @@ class SourceFile(GtkSource.File):
self._changed,
self._mark_set,
self._insert_text,
+ self._after_insert_text,
self._modified_changed
)
@@ -45,17 +47,38 @@ class SourceFile(GtkSource.File):
event.file = self
self.emit(event)
- def _insert_text(self, buffer: SourceBuffer, location: Gtk.TextIter,
+ def _insert_text(
+ self,
+ buffer: SourceBuffer,
+ location: Gtk.TextIter,
+ text: str, length: int
+ ):
+ ...
+
+ def _after_insert_text(
+ self,
+ buffer: SourceBuffer,
+ location: Gtk.TextIter,
text: str, length: int
):
event = Event_Factory.create_event(
"text_inserted",
- file = self,
- buffer = buffer
+ file = self,
+ buffer = self.buffer,
+ location = location,
+ text = text,
+ length = length
)
- self.emit(event)
- def _mark_set(self, buffer: SourceBuffer, location: Gtk.TextIter,
+ # Note: 'idle_add' needed b/c markers don't get thir positions
+ # updated relative to the initial insert.
+ # If not used, seg faults galor during multi insert.
+ GLib.idle_add(self.emit, event)
+
+ def _mark_set(
+ self,
+ buffer: SourceBuffer,
+ location: Gtk.TextIter,
mark: Gtk.TextMark
):
# event = Event_Factory.create_event(
@@ -108,6 +131,12 @@ class SourceFile(GtkSource.File):
self.emit(event)
def save(self):
+ event = Event_Factory.create_event(
+ "saved_file",
+ file = self, buffer = self.buffer
+ )
+
+ self.emit(event)
self._write_file( self.get_location() )
def save_as(self):
diff --git a/src/core/widgets/code/source_view.py b/src/core/widgets/code/source_view.py
index ce48f6b..bbb752d 100644
--- a/src/core/widgets/code/source_view.py
+++ b/src/core/widgets/code/source_view.py
@@ -10,14 +10,18 @@ from gi.repository import GLib
from gi.repository import GtkSource
# Application imports
+from libs.dto.states import SourceViewStates
+
from .mixins.source_view_dnd_mixin import SourceViewDnDMixin
class SourceView(GtkSource.View, SourceViewDnDMixin):
- def __init__(self):
+ def __init__(self, state: SourceViewStates = SourceViewStates.INSERT):
super(SourceView, self).__init__()
+ self.state = state
+
self._cut_temp_timeout_id = None
self._cut_buffer = ""
diff --git a/src/core/widgets/webkit/webkit_ui.py b/src/core/widgets/webkit/webkit_ui.py
index d6476c2..749a717 100644
--- a/src/core/widgets/webkit/webkit_ui.py
+++ b/src/core/widgets/webkit/webkit_ui.py
@@ -44,6 +44,7 @@ class WebkitUI(WebKit2.WebView):
data = f.read()
self.load_html(content = data, base_uri = f"file://{path}/")
+ # self.load_uri("https://duckduckgo.com/")
def _setup_content_manager(self):
content_manager = self.get_user_content_manager()
diff --git a/src/libs/controllers/controller_base.py b/src/libs/controllers/controller_base.py
index fe2e5cd..88d564d 100644
--- a/src/libs/controllers/controller_base.py
+++ b/src/libs/controllers/controller_base.py
@@ -3,7 +3,7 @@
# Lib imports
# Application imports
-from libs.singleton import Singleton
+from ..singleton import Singleton
from ..dto.base_event import BaseEvent
@@ -25,7 +25,7 @@ class ControllerBase(Singleton, EmitDispatcher):
def _controller_message(self, event: BaseEvent):
- raise ControllerBaseException("Controller Base must override '_controller_message'...")
+ raise ControllerBaseException("Controller Base '_controller_message' must be overridden...")
def set_controller_context(self, controller_context: ControllerContext):
self.controller_context = controller_context
diff --git a/src/libs/controllers/controller_manager.py b/src/libs/controllers/controller_manager.py
index e7d010f..1288b2b 100644
--- a/src/libs/controllers/controller_manager.py
+++ b/src/libs/controllers/controller_manager.py
@@ -24,7 +24,7 @@ class ControllerManager(Singleton, dict):
def _crete_controller_context(self) -> ControllerContext:
controller_context = ControllerContext()
controller_context.message_to = self.message_to
- controller_context.message = self.message
+ controller_context.message = self.message
return controller_context
diff --git a/src/libs/dto/code/__init__.py b/src/libs/dto/code/__init__.py
index c3c48a4..b814883 100644
--- a/src/libs/dto/code/__init__.py
+++ b/src/libs/dto/code/__init__.py
@@ -4,6 +4,8 @@
from .code_event import CodeEvent
+from .register_provider_event import RegisterProviderEvent
+
from .get_command_system_event import GetCommandSystemEvent
from .request_completion_event import RequestCompletionEvent
from .cursor_moved_event import CursorMovedEvent
@@ -18,6 +20,7 @@ from .added_new_file_event import AddedNewFileEvent
from .swapped_file_event import SwappedFileEvent
from .popped_file_event import PoppedFileEvent
from .removed_file_event import RemovedFileEvent
+from .saved_file_event import SavedFileEvent
from .get_file_event import GetFileEvent
from .get_swap_file_event import GetSwapFileEvent
diff --git a/src/libs/dto/code/register_provider_event.py b/src/libs/dto/code/register_provider_event.py
new file mode 100644
index 0000000..b0a0b38
--- /dev/null
+++ b/src/libs/dto/code/register_provider_event.py
@@ -0,0 +1,19 @@
+# Python imports
+from dataclasses import dataclass, field
+
+# Lib imports
+import gi
+gi.require_version('GtkSource', '4')
+
+from gi.repository import GtkSource
+
+# Application imports
+from ..base_event import BaseEvent
+
+
+
+@dataclass
+class RegisterProviderEvent(BaseEvent):
+ provider_name: str = ""
+ provider: GtkSource.CompletionProvider = None
+ language_ids: list = field(default_factory=lambda: [])
diff --git a/src/libs/dto/code/saved_file_event.py b/src/libs/dto/code/saved_file_event.py
new file mode 100644
index 0000000..42636ec
--- /dev/null
+++ b/src/libs/dto/code/saved_file_event.py
@@ -0,0 +1,13 @@
+# Python imports
+from dataclasses import dataclass, field
+
+# Lib imports
+
+# Application imports
+from .code_event import CodeEvent
+
+
+
+@dataclass
+class SavedFileEvent(CodeEvent):
+ ...
diff --git a/src/libs/dto/code/text_inserted_event.py b/src/libs/dto/code/text_inserted_event.py
index 30f5f2e..8bdbe96 100644
--- a/src/libs/dto/code/text_inserted_event.py
+++ b/src/libs/dto/code/text_inserted_event.py
@@ -2,6 +2,11 @@
from dataclasses import dataclass, field
# Lib imports
+import gi
+
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import Gtk
# Application imports
from .code_event import CodeEvent
@@ -10,6 +15,6 @@ from .code_event import CodeEvent
@dataclass
class TextInsertedEvent(CodeEvent):
- line: int = 0
- char: int = 0
- value: str = ""
+ location: Gtk.TextIter = None
+ text: str = ""
+ length: int = 0
diff --git a/src/libs/dto/states/__init__.py b/src/libs/dto/states/__init__.py
new file mode 100644
index 0000000..1f05351
--- /dev/null
+++ b/src/libs/dto/states/__init__.py
@@ -0,0 +1,7 @@
+"""
+ Code DTO States Package
+"""
+
+from .source_view_states import SourceViewStates
+from .cursor_action import CursorAction
+from .move_direction import MoveDirection
diff --git a/src/libs/dto/states/cursor_action.py b/src/libs/dto/states/cursor_action.py
new file mode 100644
index 0000000..810561d
--- /dev/null
+++ b/src/libs/dto/states/cursor_action.py
@@ -0,0 +1,14 @@
+# Python imports
+from enum import Enum
+
+# Lib imports
+
+# Application imports
+
+
+
+class CursorAction(Enum):
+ NONE = 0
+ DELETE = 1
+ BACKSPACE = 2
+ ENTER = 3
diff --git a/src/libs/dto/states/move_direction.py b/src/libs/dto/states/move_direction.py
new file mode 100644
index 0000000..9ef314a
--- /dev/null
+++ b/src/libs/dto/states/move_direction.py
@@ -0,0 +1,15 @@
+# Python imports
+from enum import Enum
+
+# Lib imports
+
+# Application imports
+
+
+
+class MoveDirection(Enum):
+ NONE = 0
+ UP = 1
+ DOWN = 2
+ LEFT = 3
+ RIGHT = 4
diff --git a/src/libs/dto/states/source_view_states.py b/src/libs/dto/states/source_view_states.py
new file mode 100644
index 0000000..b1c3c69
--- /dev/null
+++ b/src/libs/dto/states/source_view_states.py
@@ -0,0 +1,14 @@
+# Python imports
+from enum import Enum
+
+# Lib imports
+
+# Application imports
+
+
+
+class SourceViewStates(Enum):
+ INSERT = 0
+ MULTIINSERT = 1
+ COMMAND = 2
+ READONLY = 3
diff --git a/src/libs/settings/other/webkit_ui_settings.py b/src/libs/settings/other/webkit_ui_settings.py
index 981ea49..955860c 100644
--- a/src/libs/settings/other/webkit_ui_settings.py
+++ b/src/libs/settings/other/webkit_ui_settings.py
@@ -39,4 +39,5 @@ class WebkitUISettings(WebKit2.Settings):
self.set_enable_webaudio(True)
self.set_enable_accelerated_2d_canvas(True)
- self.set_user_agent(f"{APP_NAME}")
\ No newline at end of file
+ self.set_user_agent(f"Mozilla/5.0 (macOS, AArch64) {APP_NAME}/1.0 Chrome/140.0.0 AppleWebKit/537.36 Safari/537.36")
+
diff --git a/src/plugins/controller.py b/src/plugins/controller.py
index 588798c..ead0ebf 100644
--- a/src/plugins/controller.py
+++ b/src/plugins/controller.py
@@ -55,7 +55,7 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
for file in os.listdir(path):
_path = os.path.join(path, file)
if os.path.isdir(_path):
- self.collect_search_locations(_path, locations)
+ self._collect_search_locations(_path, locations)
def _load_plugins(
self,
@@ -70,7 +70,7 @@ class PluginsController(ControllerBase, PluginsControllerMixin, PluginReloadMixi
try:
target = join(path, "plugin.py")
if not os.path.exists(target):
- raise InvalidPluginException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
+ raise PluginsControllerException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
module = self._load_plugin_module(path, folder, target)
diff --git a/src/plugins/manifest_manager.py b/src/plugins/manifest_manager.py
index f065c0f..e41b92d 100644
--- a/src/plugins/manifest_manager.py
+++ b/src/plugins/manifest_manager.py
@@ -32,11 +32,8 @@ class ManifestManager:
for path, folder in [
[join(self._plugins_path, item), item]
- if
- os.path.isdir( join(self._plugins_path, item) )
- else
- None
for item in os.listdir(self._plugins_path)
+ if os.path.isdir( join(self._plugins_path, item) )
]:
self.load(folder, path)
diff --git a/src/plugins/plugin_types/__init__.py b/src/plugins/plugin_types/__init__.py
new file mode 100644
index 0000000..5814687
--- /dev/null
+++ b/src/plugins/plugin_types/__init__.py
@@ -0,0 +1,7 @@
+"""
+ Plugin Types Module
+"""
+
+from .plugin_base import PluginBase
+from .plugin_ui import PluginUI
+from .plugin_code import PluginCode
diff --git a/src/plugins/plugin_types/plugin_base.py b/src/plugins/plugin_types/plugin_base.py
new file mode 100644
index 0000000..1b1b97a
--- /dev/null
+++ b/src/plugins/plugin_types/plugin_base.py
@@ -0,0 +1,49 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.dto.base_event import BaseEvent
+
+from ..plugin_context import PluginContext
+
+
+
+class PluginBaseException(Exception):
+ ...
+
+
+
+class PluginBase:
+ def __init__(self, *args, **kwargs):
+ super(PluginBase, self).__init__(*args, **kwargs)
+
+ self.plugin_context: PluginContext = None
+
+
+ def _controller_message(self, event: BaseEvent):
+ raise PluginBaseException("Plugin Base '_controller_message' must be overriden by Plugin")
+
+ def load(self):
+ raise PluginBaseException("Plugin Base 'load' must be overriden by Plugin")
+
+ def run(self):
+ raise PluginBaseException("Plugin Base 'run' must be overriden by Plugin")
+
+ def requests_ui_element(self, element_id: str):
+ raise PluginBaseException("Plugin Base 'requests_ui_element' must be overriden by Plugin")
+
+ def message(self, event: BaseEvent):
+ raise PluginBaseException("Plugin Base 'message' must be overriden by Plugin")
+
+ def message_to(self, name: str, event: BaseEvent):
+ raise PluginBaseException("Plugin Base 'message_to' must be overriden by Plugin")
+
+ def message_to_selected(self, names: list[str], event: BaseEvent):
+ raise PluginBaseException("Plugin Base 'message_to_selected' must be overriden by Plugin")
+
+ def emit(self, event_type: str, data: tuple = ()):
+ raise PluginBaseException("Plugin Base 'emit' must be overriden by Plugin")
+
+ def emit_and_await(self, event_type: str, data: tuple = ()):
+ raise PluginBaseException("Plugin Base 'emit_and_await' must be overriden by Plugin")
diff --git a/src/plugins/plugin_types/plugin_code.py b/src/plugins/plugin_types/plugin_code.py
new file mode 100644
index 0000000..37438c1
--- /dev/null
+++ b/src/plugins/plugin_types/plugin_code.py
@@ -0,0 +1,41 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+from libs.dto.base_event import BaseEvent
+
+from ..plugin_context import PluginContext
+from .plugin_base import PluginBase
+
+
+
+class PluginCodeException(Exception):
+ ...
+
+
+
+class PluginCode(PluginBase):
+ def __init__(self, *args, **kwargs):
+ super(PluginCode, self).__init__(*args, **kwargs)
+
+ self.plugin_context: PluginContext = None
+
+
+ def _controller_message(self, event: BaseEvent):
+ raise PluginCodeException("Plugin Code '_controller_message' must be overriden by Plugin")
+
+ def load(self):
+ raise PluginCodeException("Plugin Code 'load' must be overriden by Plugin")
+
+ def run(self):
+ raise PluginCodeException("Plugin Code 'run' must be overriden by Plugin")
+
+ def message(self, event: BaseEvent):
+ return self.plugin_context.message(event)
+
+ def message_to(self, name: str, event: BaseEvent):
+ return self.plugin_context.message_to(name, event)
+
+ def message_to_selected(self, names: list[str], event: BaseEvent):
+ return self.plugin_context.message_to_selected(names, event)
diff --git a/src/plugins/plugin_base.py b/src/plugins/plugin_types/plugin_ui.py
similarity index 68%
rename from src/plugins/plugin_base.py
rename to src/plugins/plugin_types/plugin_ui.py
index 08a1ccc..f5e3534 100644
--- a/src/plugins/plugin_base.py
+++ b/src/plugins/plugin_types/plugin_ui.py
@@ -5,30 +5,31 @@
# Application imports
from libs.dto.base_event import BaseEvent
-from .plugin_context import PluginContext
+from ..plugin_context import PluginContext
+from .plugin_base import PluginBase
-class PluginBaseException(Exception):
+class PluginCodeException(Exception):
...
-class PluginBase:
+class PluginUI(PluginBase):
def __init__(self, *args, **kwargs):
- super(PluginBase, self).__init__(*args, **kwargs)
+ super(PluginUI, self).__init__(*args, **kwargs)
self.plugin_context: PluginContext = None
def _controller_message(self, event: BaseEvent):
- raise PluginBaseException("Plugin Base '_controller_message' must be overriden by Plugin")
+ raise PluginCodeException("Plugin UI '_controller_message' must be overriden by Plugin")
def load(self):
- raise PluginBaseException("Plugin Base 'load' must be overriden by Plugin")
+ raise PluginCodeException("Plugin UI 'load' must be overriden by Plugin")
def run(self):
- raise PluginBaseException("Plugin Base 'run' must be overriden by Plugin")
+ raise PluginCodeException("Plugin UI 'run' must be overriden by Plugin")
def requests_ui_element(self, element_id: str):
return self.plugin_context.requests_ui_element(element_id)
diff --git a/src/plugins/plugins_controller_mixin.py b/src/plugins/plugins_controller_mixin.py
index 0691525..3583ee3 100644
--- a/src/plugins/plugins_controller_mixin.py
+++ b/src/plugins/plugins_controller_mixin.py
@@ -6,6 +6,10 @@
+class InvalidPluginException(Exception):
+ ...
+
+
class PluginsControllerMixin:
diff --git a/user_config/usr/share/app_name/stylesheet.css b/user_config/usr/share/app_name/stylesheet.css
index d8d272c..fef2246 100644
--- a/user_config/usr/share/app_name/stylesheet.css
+++ b/user_config/usr/share/app_name/stylesheet.css
@@ -1,21 +1,56 @@
-/* ---- Make most desired things base transparent ---- */
+/* ---- Overrides ---- */
+text selection {
+ background-color: #44475a;
+ color: #FFA500;
+}
/* ---- Make most desired things base transparent ---- */
popover,
popover > box,
notebook,
header,
-stack,
-scrolledwindow,
-viewport {
+stack {
background: rgba(0, 0, 0, 0.0);
color: rgba(255, 255, 255, 1);
}
-scrolledwindow * {
- background: rgba(0, 0, 0, 0.0);
+
+/* The scrolled window itself */
+scrolledwindow,
+scrolledwindow viewport,
+textview,
+textview text {
+ background-color: transparent;
}
+scrollbar {
+ background-color: #2a2a2a;
+}
+
+/* The track */
+scrollbar trough {
+ background-color: transparent;
+ /*background-color: #2a2a2a;*/
+ border-radius: 6px;
+}
+
+/* The draggable handle */
+scrollbar slider {
+ background-color: #555;
+ border-radius: 6px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+scrollbar slider:hover {
+ background-color: #777;
+}
+
+scrollbar slider:active {
+ background-color: #999;
+}
+
+
border {
background: rgba(39, 43, 52, 0.84);
}
@@ -41,6 +76,20 @@ tab:checked {
color: rgba(255, 255, 255, 1);
}
+
+/* the mini view container of text */
+.mini-view > text {
+ background: rgba(39, 43, 52, 0.64);
+}
+
+/* draggable overlay of the miniview */
+.mini-view > * {
+ background-color: rgba(124, 124, 124, 0.34);
+ color: rgba(255, 255, 255, 1);
+ font-size: 1px;
+}
+
+
.base-container {
margin: 10px;
}
@@ -96,177 +145,9 @@ tab:checked {
color: rgba(255, 0, 0, 0.64);
}
-
-
-
-
-
-
-
-
-
-/*popover,*/
-/*popover > box*/
-/*.main-window > box,*/
-/*.main-window > box > box,*/
-/*.main-window > box > box > paned,*/
-/*.main-window > box > box > paned > notebook,*/
-/*.main-window > box > box > paned > notebook > stack,*/
-/*.main-window > box > box > paned > notebook > stack > scrolledwindow > textview > * {*/
-/* background: rgba(0, 0, 0, 0.0);*/
-/* color: rgba(255, 255, 255, 1);*/
-/*}*/
-
-
-/* ---- top controls ---- */
-/*.main-window > box > box > button,*/
-/*.main-window > box > box > buttonbox > button {*/
-/* background: rgba(39, 43, 52, 0.64);*/
-/* margin-left: 12px;*/
-/* margin-right: 12px;*/
-/*}*/
-
-
-/* status bar */
-/*.main-window > box > statusbar {*/
-/* background: rgba(39, 43, 52, 0.64);*/
-/* padding-left: 96px;*/
-/* padding-right: 96px;*/
-/*}*/
-
-
-/* ---- notebook headers ---- */
-/*notebook > header {*/
-/* background: rgba(39, 43, 52, 0.72);*/
-/*}*/
-
-/*notebook > header > tabs > tab {*/
-/* color: rgba(255, 255, 255, 1);*/
-/*}*/
-
-/*notebook > header > tabs > tab:active {*/
-/* background: rgba(0, 0, 0, 0.0);*/
-/*}*/
-
-
-/* ---- notebook tab buttons ---- */
-/*tab > box > button {*/
-/* background: rgba(116, 0, 0, 0.64);*/
-/*}*/
-
-/*tab > box > button:hover {*/
-/* background: rgba(256, 0, 0, 0.64);*/
-/*}*/
-
-/*notebook > header > tabs > tab:checked {*/
- /* Neon Blue 00e8ff */
- /* background-color: rgba(0, 232, 255, 0.2); */
-
-/* background-color: rgba(255, 255, 255, 0.46);*/
-
- /* Dark Bergundy */
- /* background-color: rgba(116, 0, 0, 0.25); */
-
-/* color: rgba(255, 255, 255, 0.8);*/
-/*}*/
-
-/*.notebook-selected-focus {*/
- /* Neon Blue 00e8ff border */
- /* border: 2px solid rgba(0, 232, 255, 0.34); */
-
-/* border: 2px solid rgba(255, 255, 255, 0.46);*/
-
- /* Dark Bergundy */
- /* border: 2px solid rgba(116, 0, 0, 0.64); */
-/*}*/
-
-
-/* ---- source code notebooks ---- */
-/*notebook > stack > scrolledwindow > textview {*/
-/* background: rgba(39, 43, 52, 0.64);*/
-/* color: rgba(255, 255, 255, 1);*/
-/*}*/
-
-/*textview {*/
-/* padding-bottom: 50em;*/
-/*}*/
-
-
-/* any popover */
-/*popover {*/
-/* background: rgba(39, 43, 52, 0.86);*/
-/* color: rgba(255, 255, 255, 1);*/
-/*}*/
-
-/* ---- make text selection slightly transparent ---- */
-/** selection {*/
-/* background-color: rgba(0, 115, 115, 0.34);*/
- /* Bergundy */
- /* background-color: rgba(116, 0, 0, 0.64); */
-/* color: rgba(255, 255, 255, 0.5);*/
-/*}*/
-
-
-/* ---- miniview properties ---- */
-/*.source-view,*/
-/*.mini-view {*/
-/* font-family: Monospace;*/
-/*}*/
-
-/* the mini view container of text */
-/*.mini-view > text {*/
-/* background: rgba(39, 43, 52, 0.64);*/
-/*}*/
-
-/* draggable overlay of the miniview */
-/*.mini-view > * {*/
-/* background: rgba(64, 64, 64, 0.32);*/
-/* color: rgba(255, 255, 255, 1);*/
-/* font-size: 1px;*/
-/*}*/
-
-/*.mini-view > text {*/
-/* background: rgba(39, 43, 52, 0.64);*/
-/*}*/
-
-
-/* other properties */
-/*.buffer_changed {*/
-/* color: rgba(255, 168, 0, 1.0);*/
- /* color: rgba(0, 232, 255, 0.64); */
-/*}*/
-
-
-/*.searching,*/
-/*.search_success,*/
-/*.search_fail {*/
-/* border-style: solid;*/
-/*}*/
-
-/*.searching {*/
-/* border-color: rgba(0, 225, 225, 0.64);*/
-/*}*/
-/*.search_success {*/
-/* background: rgba(136, 204, 39, 0.12);*/
-/* border-color: rgba(136, 204, 39, 1);*/
-/*}*/
-/*.search_fail {*/
-/* background: rgba(170, 18, 18, 0.12);*/
-/* border-color: rgba(200, 18, 18, 1);*/
-/*}*/
-
-
-
-
-
-
-
-
-
-
-.error_txt { color: rgb(170, 18, 18); }
-.warning_txt { color: rgb(255, 168, 0); }
-.success_txt { color: rgb(136, 204, 39); }
+.error-txt { color: rgb(170, 18, 18); }
+.warning-txt { color: rgb(255, 168, 0); }
+.success-txt { color: rgb(136, 204, 39); }