Compare commits

...

63 Commits

Author SHA1 Message Date
6c42ff7c7d Made words completer run async on load and update; swapped out tabs to use Gtk.Notebook 2026-02-16 17:11:30 -06:00
b922415f98 Moved completers to new dir; improved completers and added word completion 2026-02-16 01:39:23 -06:00
5273c58ed6 Major completion provider overhaul; pluigin load and pattern improvements; css overhaul/cleanup; source view state modes added 2026-02-14 16:15:54 -06:00
4ce4d85842 removed endpoint_registery; relocated builder_wrapper to be widget_registery under libs/ 2026-01-19 13:57:44 -06:00
c6b10ceb17 defaulting to buffer if language not detected 2026-01-19 00:29:10 -06:00
a036dc428b Wiring plugins to controller messages; importing plugin controller to code base; fixed VTE widget adding to bash history 2026-01-18 22:39:52 -06:00
b8ce6e160a Pligins refactor with new context and controller integration 2026-01-18 20:36:17 -06:00
e2f29207ba Preliminary controller layout work for plugin integration; controller base message refactor naming 2026-01-18 13:53:29 -06:00
99f1bffefb Restructuring controller to broaden usability; changed events logic to support that 2026-01-18 00:52:54 -06:00
f7d944f7a9 Created libs.code package and moved pertinant DTOs to it as well as widget.code that can go there too 2026-01-13 11:23:26 -06:00
4469e8189f Improved folder structure for commamd system and controllers 2026-01-13 00:43:51 -06:00
a507469bf8 Added controller manager registery guard; localized event type access for code to event factory 2026-01-12 23:59:13 -06:00
a07123d165 fix singleton typeing and __init__ 2026-01-12 23:34:34 -06:00
00c72a7117 Fixed code view close_file command 2026-01-12 00:15:43 -06:00
f8d73ad74a Fixed code view expansion issues; fixed pre/post code view key mappings not sinking Gtk signals properly 2026-01-12 00:07:44 -06:00
04e0c3caf6 Moved to using event_factory and referencing types from it 2026-01-11 22:44:03 -06:00
a57bfd94fc Created helpers file for commands to reduce duplication 2026-01-11 18:30:51 -06:00
c2060963cc Full 'code' widget refactor to utilize controllers and cross controller event signaling 2026-01-11 18:01:30 -06:00
8253e250d8 fixed pyright pathing; exception typo; and path manager arg typo 2026-01-04 13:27:22 -06:00
be608f645e Added suppression of some errors we don't care about 2026-01-04 00:15:36 -06:00
356da04f46 Improved some exception handling 2026-01-04 00:04:38 -06:00
48182f9775 Created a settings > path_manager class and cleaned up srtting manager class 2026-01-03 23:28:14 -06:00
90b2f050c6 Improved base pligin implementation 2026-01-03 21:57:37 -06:00
79375d34b1 Added undo, redo, go-to, and duplicate lines commands; updated styles for line highlight color; cleanup 2025-12-29 02:20:55 -06:00
5ee587e205 Added temp cut/paste buffer commands; added zoom in/out commands; fixed tab closures leaking memory 2025-12-29 00:46:10 -06:00
3fc39042f1 Wired tabs close and selection; improved files manager next index checking; code event focus ignore attrib added 2025-12-28 22:44:44 -06:00
e18be655e8 Code Widget refactor; observable refactor 2025-12-28 19:53:05 -06:00
12ada8568e Added code load start files command; improved show logic for code widget 2025-12-24 22:31:41 -06:00
4c179974c0 Added dnd commands; moved source view events to mixin; improved/separated pop logic for files manager; added observer pattern for active file for buffer 2025-12-20 01:10:53 -06:00
6acbcb53c2 Moving settings out of global space; added editor commands and wired them 2025-12-16 20:25:24 -06:00
ce22ed6a53 Updated code key bindings 2025-12-15 22:55:55 -06:00
c16493037d Added more commands to code; refactored container classes; general cleanup 2025-12-15 22:50:28 -06:00
849e7611ca Added code view widget and base command system; updated keybinding system 2025-12-14 03:20:03 -06:00
2c453bc621 Fixed IPC issues with passing paths back to instance 2025-12-10 20:09:32 -06:00
530fe7c3ab Updated jeybinding and restructured css 2025-11-30 19:51:04 -06:00
5e5cc50ba8 Fixed empty vte commit error 2025-11-30 00:59:37 -06:00
22736147e6 added clock, pager, and task list widgets for ref; added footer container; added option for key combo o universally toggle window with key combo if set 2025-11-29 23:22:03 -06:00
4c25ce297c Corrected css for popover box 2025-10-21 22:21:06 -05:00
60b55da973 Moved to use contextlib suppress pattern than empty exception blocks 2025-10-21 22:20:06 -05:00
7c0d87fd20 Slight call start restructuring, fixing css for popover box 2025-08-25 00:22:42 -05:00
4cd5a1f089 Fixing readme n requirements 2025-07-13 14:17:52 -05:00
18dc71dcb0 Adding generic doc string basecontroller 2025-07-13 14:15:25 -05:00
dd3e87f636 Adding call chain wrapper; cleanup; optimization 2024-12-22 00:50:32 -06:00
cca007db76 Added VTE widget; css changes; format cleanup 2024-11-08 22:46:07 -06:00
33c0827ca2 Moved args info to settings and restructured calls 2024-10-20 16:03:19 -05:00
f2b33066af Added stronger typing in settings; logging loading times; css changes to transparency 2024-10-20 15:20:18 -05:00
fafc1a985f Updated __init__ files; theming css changes, other 2024-10-14 21:57:18 -05:00
e2e9dc8c1f made default hiding transparency controls easier with restructured show calls 2024-08-31 22:27:11 -05:00
25b6b5305b update readme 2024-07-26 19:53:04 -05:00
62debf9ece extending plugins to load pre or post app start 2024-06-29 21:24:57 -05:00
cc5966dab2 Added status icon for system trays 2024-05-06 19:11:18 -05:00
e82e4fb1eb Updated a requirement 2024-05-03 01:11:52 -05:00
48bac7e791 Added a requirement; added a debug handler 2024-05-03 01:08:49 -05:00
b78fac0aa5 Fixed depricated exception type; fixed siguser type 2024-03-25 22:48:05 -05:00
bdb9c157f7 moved controls to dir; added open files button example; css transparency changes 2024-03-16 22:36:04 -05:00
a0f32a7c00 app_name --> APP_NAME 2024-02-29 18:50:34 -06:00
b1096055b7 Renamed settings folder; hyphenated event names 2024-02-25 16:19:14 -06:00
ea62eb280c Added builder wrapper due to plugin setup 2024-02-24 21:55:40 -06:00
cc8f62776d Fixed bindings; added bindings 2024-02-23 23:45:13 -06:00
2389f1d414 moved db stuff to own folder 2024-02-21 22:15:25 -06:00
d81bf3815a Removed logging item 2024-02-21 20:34:42 -06:00
1695f5bc5c Overode method to add debug logging 2024-02-16 20:43:09 -06:00
1245951da9 Cleanup of starting file collection 2024-02-16 20:13:36 -06:00
237 changed files with 8900 additions and 958 deletions

View File

@@ -3,11 +3,13 @@ A template project for Python with Gtk applications.
### Requirements ### Requirements
* PyGObject (Gtk introspection library) * PyGObject (Gtk introspection library)
* pygobject-stubs (For actually getting pylsp or python-language-server to auto complete in LSPs. Do if GTK3 --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2)
* pyxdg (Desktop ".desktop" file parser) * pyxdg (Desktop ".desktop" file parser)
* setproctitle (Define process title to search and kill more easily) * setproctitle (Define process title to search and kill more easily)
* sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy) * sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy)
### Note ### Note
* pyrightconfig.json can prompt IDEs that use pyright lsp on where imports are located- look at venvPath and venv. "venvPath" is parent path of "venv" where "venv" is just the name of the folder under the parent path that is the python created venv.
* Move respetive sub folder content under user_config to the same places in Linux. Though, user/share/<app name> can go to ~/.config folder if prefered. * Move respetive sub folder content under user_config to the same places in Linux. Though, user/share/<app name> can go to ~/.config folder if prefered.
* In additiion, place the plugins folder in the same app folder you moved to /usr/share/<app name> or ~/.config/<app name> . * In additiion, place the plugins folder in the same app folder you moved to /usr/share/<app name> or ~/.config/<app name> .
There are a "\<change_me\>" strings and files that need to be set according to your app's name located at: There are a "\<change_me\>" strings and files that need to be set according to your app's name located at:

View File

@@ -1,2 +1,31 @@
### Note ### Note
Copy the example and rename it to your desired name. The Main class and passed in arguments are required. You don't necessarily need to use the passed in socket_id or event_system. Copy the example and rename it to your desired name. Plugins define a ui target slot with the 'ui_target' requests data but don't have to if not directly interacted with.
Plugins must have a run method defined; though, you do not need to necessarily do anything within it. The run method implies that the passed in event system or other data is ready for the plugin to use.
### Manifest Example (All are required even if empty.)
```
class Manifest:
name: str = "Example Plugin"
author: str = "John Doe"
version: str = "0.0.1"
support: str = ""
pre_launch: bool = False
requests: {} = {
'pass_ui_objects': ["plugin_control_list"],
'pass_events': True,
'bind_keys': []
}
```
### Requests
```
requests: {} = {
'pass_events': true, # If empty or not present will be ignored.
"pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin.
'bind_keys': [f"{name}||send_message:<Control>f"],
f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
}
```

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,7 @@
{
"name": "Example Completer",
"author": "John Doe",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -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 = "Example Completer",
provider = self.provider,
language_ids = []
)
self.message_to("completion", event)
def run(self):
...

View File

@@ -0,0 +1,76 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import GtkSource
# Application imports
from .provider_response_cache import ProviderResponseCache
class Provider(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):
super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def do_get_name(self):
""" Returns: a new string containing the name of the provider. """
return 'Example Code Completion'
def do_match(self, context):
# word = context.get_word()
# if not word or len(word) < 2: return False
""" Get whether the provider match the context of completion detailed in context. """
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
return True
def do_get_priority(self):
""" Determin position in result list along other providor results. """
return 5
def do_activate_proposal(self, proposal, iter_):
""" Manually handle actual completion insert or set flags and handle normally. """
buffer = iter_.get_buffer()
# Note: Flag mostly intended for SourceViewsMultiInsertState
# to insure marker processes inserted text correctly.
buffer.is_processing_completion = True
return False
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.USER_REQUESTED | GtkSource.CompletionActivation.INTERACTIVE
return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context):
results = self.response_cache.filter_with_context(context)
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)

View File

@@ -0,0 +1,109 @@
# 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 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
class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
self.matchers: dict = {
"hello": {
"label": "Hello, World!",
"text": "Hello, World!",
"info": GLib.markup_escape_text( "<b>Says the first ever program developers write...</b>" )
},
"foo": {
"label": "foo",
"text": "foo }}"
},
"bar": {
"label": "bar",
"text": "bar }}"
}
}
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) -> list[dict]:
...
def filter_with_context(self, context) -> list[dict]:
"""
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: list[dict] = [
{
"label": self.matchers[ "hello" ]["label"],
"text": self.matchers[ "hello" ]["text"],
"info": self.matchers[ "hello" ]["info"]
}
]
# 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 not end_iter: return
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):
# optionally proposed based on left search via regex
proposals.append(
{
"label": self.matchers[ "foo" ]["label"],
"text": self.matchers[ "foo" ]["text"],
"info": ""
}
)
# optionally proposed based on left search via regex
proposals.append(
{
"label": self.matchers[ "bar" ]["label"],
"text": self.matchers[ "bar" ]["text"],
"info": ""
}
)
return proposals

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,7 @@
{
"name": "LSP Completer",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -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):
...

View File

@@ -0,0 +1,82 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import GtkSource
# Application imports
from .provider_response_cache import ProviderResponseCache
class Provider(GObject.GObject, 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):
super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def pre_populate(self, context):
...
def do_get_name(self):
return "LSP Code Completion"
def do_match(self, context):
word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False
iter = self.response_cache.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_activate_proposal(self, proposal, iter_):
buffer = iter_.get_buffer()
# Note: Flag mostly intended for SourceViewsMultiInsertState
# to insure marker processes inserted text correctly.
buffer.is_processing_completion = True
return False
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):
results = self.response_cache.filter_with_context(context)
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)

View File

@@ -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) -> list[dict]:
return []
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
proposals = [
{
"label": "LSP Class",
"text": "LSP Code",
"info": "A test LSP completion item..."
}
]
return proposals

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,3 @@
from .parser import load, loads
from .writer import dump, dumps
from .speg import ParseError

View File

@@ -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

View File

@@ -0,0 +1 @@
from .peg import peg, ParseError

View File

@@ -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__

View File

@@ -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)

View File

@@ -0,0 +1,7 @@
{
"name": "Snippets Completer",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -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):
...

View File

@@ -0,0 +1,68 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GObject
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):
super(Provider, self).__init__()
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_activate_proposal(self, proposal, iter_):
buffer = iter_.get_buffer()
# Note: Flag mostly intended for SourceViewsMultiInsertState
# to insure marker processes inserted text correctly.
buffer.is_processing_completion = True
return False
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)

View File

@@ -0,0 +1,68 @@
# 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) -> list[dict]:
response: list[dict] = []
for entry in self.matchers:
if not word in entry: continue
data = self.matchers[entry]
response.append(data)
return response
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
response: list[dict] = []
return response

View File

@@ -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': """<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="fave_icon.png">
<link rel="stylesheet" href="resources/css/base.css">
<link rel="stylesheet" href="resources/css/main.css">
</head>
<body>
<script src="resources/js/.js" charset="utf-8"></script>
<script src="resources/js/.js" charset="utf-8"></script>
</body>
</html>
"""
'Canvas Tag':
'prefix': 'canvas'
'body': """<canvas id="canvas" width="800" height="600" style="border:1px solid #c3c3c3;"></canvas>"""
'Img Tag':
'prefix': 'img'
'body': """<img class="" src="" alt="" />"""
'Br Tag':
'prefix': 'br'
'body': """<br/>"""
'Hr Tag':
'prefix': 'hr'
'body': """<hr/>"""
'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 === "<yourDataStringToLookFor>") {
// 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 === "<yourDataStringToLookFor>") {
// 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
// Start the session
session_start();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "data:dataToReturn\\\\n\\\\n";
flush();
?>
"""
'PHP Template':
'prefix': 'php'
'body': """<?php
// Start the session
session_start();
// Determin action
chdir("../../"); // Note: If in resources/php/
if (isset($_POST['yourPostID'])) {
// code here
} else {
$message = "Server: [Error] --> Illegal Access Method!";
serverMessage("error", $message);
}
?>
"""
'HTML Template':
'prefix': 'html'
'body': """<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="fave_icon.png">
<link rel="stylesheet" href="resources/css/base.css">
<link rel="stylesheet" href="resources/css/main.css">
</head>
<body>
<script src="resources/js/.js" charset="utf-8"></script>
<script src="resources/js/.js" charset="utf-8"></script>
</body>
</html>
"""
### 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('<replace this>')
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):
...
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,53 @@
# 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(GtkSource.CompletionWords):
"""
This is a Words Completion Provider.
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
"""
__gtype_name__ = 'WordsCompletionProvider'
def __init__(self):
super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def do_get_name(self):
return 'Words 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 0
def do_activate_proposal(self, proposal, iter_):
buffer = iter_.get_buffer()
# Note: Flag mostly intended for SourceViewsMultiInsertState
# to insure marker processes inserted text correctly.
buffer.is_processing_completion = True
return False
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

View File

@@ -0,0 +1,7 @@
{
"name": "Words Completer",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -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 = "Words Completer",
provider = self.provider,
language_ids = []
)
self.message_to("completion", event)
def run(self):
...

View File

@@ -0,0 +1,71 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GObject
from gi.repository import GtkSource
# Application imports
from .provider_response_cache import ProviderResponseCache
class Provider(GObject.GObject, GtkSource.CompletionProvider):
"""
This is a Words Completion Provider.
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
"""
# __gtype_name__ = 'WordsCompletionProvider'
def __init__(self):
super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache()
def do_get_name(self):
return 'Words 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 0
def do_activate_proposal(self, proposal, iter_):
buffer = iter_.get_buffer()
# Note: Flag mostly intended for SourceViewsMultiInsertState
# to insure marker processes inserted text correctly.
buffer.is_processing_completion = True
return False
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_with_context(context)
# results = self.response_cache.filter(word)
# if not results:
# results = self.response_cache.filter_with_context(context)
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)

View File

@@ -0,0 +1,117 @@
# Python imports
import asyncio
# 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
class ProviderResponseCache(ProviderResponseCacheBase):
def __init__(self):
super(ProviderResponseCache, self).__init__()
self.matchers: dict = {}
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
buffer = event.file.buffer
asyncio.run( self._handle_change(buffer) )
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
self.matchers[event.file.buffer] = set()
del self.matchers[event.file.buffer]
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
...
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
buffer = event.file.buffer
asyncio.run( self._handle_change(buffer) )
async def _handle_change(self, buffer):
start_itr = buffer.get_start_iter()
end_itr = buffer.get_end_iter()
data = buffer.get_text(start_itr, end_itr, False)
if not data:
GLib.idle_add(self.load_empty_set, buffer)
return
if not buffer in self.matchers:
GLib.idle_add(self.load_as_new_set, buffer, data)
return
new_words = self.get_all_words(data)
GLib.idle_add(self.load_into_set, buffer, new_words)
def filter(self, word: str) -> list[dict]:
response: list[dict] = []
for entry in self.matchers:
if not word in entry: continue
data = self.matchers[entry]
response.append(data)
return response
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
buffer = self.get_iter_correctly(context).get_buffer()
word = self.get_word(context).rstrip()
response: list[dict] = []
for entry in self.matchers[buffer]:
if not entry.rstrip().startswith(word): continue
data = {
"label": entry,
"text": entry,
"info": ""
}
response.append(data)
return response
def load_empty_set(self, buffer):
self.matchers[buffer] = set()
def load_into_set(self, buffer, new_words):
self.matchers[buffer].update(new_words)
def load_as_new_set(self, buffer, data):
self.matchers[buffer] = self.get_all_words(data)
def get_all_words(self, data: str):
words = set()
def is_word_char(c):
return c.isalnum() or c == '_'
size = len(data)
i = 0
while i < size:
# Skip non-word characters
while i < size and not is_word_char(data[i]):
i += 1
start = i
# Consume word characters
while i < size and is_word_char(data[i]):
i += 1
word = data[start:i]
if not word: continue
words.add(word)
return words

View File

@@ -1,13 +1,9 @@
{ {
"manifest": { "name": "Example Plugin",
"name": "Example Plugin", "author": "John Doe",
"author": "John Doe", "version": "0.0.1",
"version": "0.0.1", "support": "",
"support": "", "requests": {
"requests": { "bind_keys": ["Example Plugin||send_message:<Control>f"]
"ui_target": "plugin_control_list",
"pass_events": "true",
"bind_keys": ["Example Plugin||send_message:<Control>f"]
}
} }
} }

View File

@@ -1,8 +1,4 @@
# Python imports # Python imports
import os
import threading
import subprocess
import time
# Lib imports # Lib imports
import gi import gi
@@ -10,42 +6,36 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from libs.dto.base_event import BaseEvent
from plugins.plugin_base import PluginBase from plugins.plugin_base import PluginBase
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
return wrapper
# NOTE: Threads WILL die with parent's destruction.
def daemon_threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
return wrapper
class Plugin(PluginBase): class Plugin(PluginBase):
def __init__(self): def __init__(self):
super().__init__() super(Plugin, self).__init__()
self.name = "Example Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
def generate_reference_ui_element(self): def _controller_message(self, event: BaseEvent):
button = Gtk.Button(label=self.name) ...
button.connect("button-release-event", self.send_message)
return button def load(self):
ui_element = self.requests_ui_element("plugin_control_list")
ui_element.add( self.generate_plugin_element() )
def run(self): def run(self):
... ...
def send_message(self, widget=None, eve=None): def generate_plugin_element(self):
button = Gtk.Button(label = self.name)
button.connect("button-release-event", self.send_message)
button.show()
return button
def send_message(self, widget = None, eve = None):
message = "Hello, World!" message = "Hello, World!"
event_system.emit("display_message", ("warning", message, None)) self.emit("display_message", ("warning", message, None))

13
pyrightconfig.json Normal file
View File

@@ -0,0 +1,13 @@
{
"reportUndefinedVariable": false,
"reportUnusedVariable": false,
"reportUnusedImport": true,
"reportDuplicateImport": true,
"executionEnvironments": [
{
"root": "./src/"
}
],
"venvPath": ".",
"venv": ".venv"
}

View File

@@ -1,4 +1,7 @@
PyGObject PyGObject==3.40.1
pyxdg pygobject-stubs --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2
setproctitle setproctitle==1.2.2
sqlmodel pyxdg==0.27
psutil==5.8.0
pycryptodome==3.20.0
sqlmodel==0.0.19

View File

@@ -1,5 +1,6 @@
# Python imports # Python imports
import builtins import builtins
import traceback
import threading import threading
import sys import sys
@@ -8,10 +9,10 @@ import sys
# Application imports # Application imports
# from libs.db import DB # from libs.db import DB
from libs.event_system import EventSystem from libs.event_system import EventSystem
from libs.endpoint_registry import EndpointRegistry
from libs.keybindings import Keybindings from libs.keybindings import Keybindings
from libs.logger import Logger from libs.logger import Logger
from libs.settings_manager.manager import SettingsManager from libs.settings.manager import SettingsManager
from libs.widget_registery import WidgetRegisteryController
@@ -31,27 +32,36 @@ def daemon_threaded_wrapper(fn):
return thread return thread
return wrapper return wrapper
def call_chain_wrapper(fn):
def wrapper(*args, **kwargs):
for line in traceback.format_stack():
print( line.strip() )
return fn(*args, **kwargs)
return wrapper
# NOTE: Just reminding myself we can add to builtins two different ways... # NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()}) # __builtins__.update({"event_system": Builtins()})
builtins.app_name = "<change_me>" builtins.APP_NAME = "<change_me>"
builtins.keybindings = Keybindings() builtins.keybindings = Keybindings()
builtins.event_system = EventSystem() builtins.event_system = EventSystem()
builtins.endpoint_registry = EndpointRegistry()
builtins.settings_manager = SettingsManager() builtins.settings_manager = SettingsManager()
builtins.widget_registery = WidgetRegisteryController()
# builtins.db = DB() # builtins.db = DB()
settings_manager.load_settings() settings_manager.load_settings()
builtins.settings = settings_manager.settings builtins.logger = Logger(
builtins.logger = Logger(settings_manager.get_home_config_path(), \ settings_manager.path_manager.get_home_config_path(), \
_ch_log_lvl=settings.debugging.ch_log_lvl, \ _ch_log_lvl = settings_manager.settings.debugging.ch_log_lvl, \
_fh_log_lvl=settings.debugging.fh_log_lvl).get_logger() _fh_log_lvl = settings_manager.settings.debugging.fh_log_lvl
).get_logger()
builtins.threaded = threaded_wrapper builtins.threaded = threaded_wrapper
builtins.daemon_threaded = daemon_threaded_wrapper builtins.daemon_threaded = daemon_threaded_wrapper
builtins.call_chain = call_chain_wrapper
@@ -60,6 +70,6 @@ def custom_except_hook(exc_type, exc_value, exc_traceback):
sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.__excepthook__(exc_type, exc_value, exc_traceback)
return return
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) logger.error("Uncaught exception", exc_info = (exc_type, exc_value, exc_traceback))
sys.excepthook = custom_except_hook sys.excepthook = custom_except_hook

View File

@@ -1,3 +1,3 @@
""" """
Start of package. Src Package.
""" """

View File

@@ -17,8 +17,9 @@ from app import Application
def main(args, unknownargs): def main():
setproctitle(f'{app_name}') setproctitle(f'{APP_NAME}')
settings_manager.set_start_load_time()
if args.debug == "true": if args.debug == "true":
settings_manager.set_debug(True) settings_manager.set_debug(True)
@@ -27,7 +28,9 @@ def main(args, unknownargs):
settings_manager.set_trace_debug(True) settings_manager.set_trace_debug(True)
settings_manager.do_dirty_start_check() settings_manager.do_dirty_start_check()
Application(args, unknownargs)
app = Application()
app.run()
@@ -36,19 +39,20 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
# Add long and short arguments # Add long and short arguments
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.") 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("--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("--no-plugins", "-np", default = "false", help = "Do not load plugins.")
parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.") parser.add_argument("--new-tab", "-nt", default = "false", help = "Opens a 'New Tab' if a handler is set for it.")
parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.") parser.add_argument("--file", "-f", default = "default", help = "JUST SOME FILE ARG.")
# Read arguments (If any...) # Read arguments (If any...)
args, unknownargs = parser.parse_known_args() args, unknownargs = parser.parse_known_args()
settings_manager.set_starting_args( args, unknownargs )
try: try:
faulthandler.enable() # For better debug info faulthandler.enable() # For better debug info
main(args, unknownargs) main()
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
quit() quit()

View File

@@ -1,4 +1,5 @@
# Python imports # Python imports
from contextlib import suppress
import signal import signal
import os import os
@@ -19,46 +20,57 @@ class AppLaunchException(Exception):
class Application: class Application:
""" docstring for Application. """ """ docstring for Application. """
def __init__(self, args, unknownargs): def __init__(self):
super(Application, self).__init__() super(Application, self).__init__()
if not settings_manager.is_trace_debug():
self.load_ipc(args, unknownargs)
self.setup_debug_hook() self.setup_debug_hook()
Window(args, unknownargs).main()
def load_ipc(self, args, unknownargs): def run(self):
ipc_server = IPCServer() if not settings_manager.is_trace_debug():
if not self.load_ipc():
return
win = Window()
win.start()
def load_ipc(self):
args, \
unknownargs = settings_manager.get_starting_args()
ipc_server = IPCServer()
self.ipc_realization_check(ipc_server) self.ipc_realization_check(ipc_server)
if ipc_server.is_ipc_alive:
return True
if not ipc_server.is_ipc_alive: logger.warning(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
for arg in unknownargs + [args.new_tab,]: for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg): if os.path.isfile(arg):
message = f"FILE|{arg}" message = f"FILE|{arg}"
ipc_server.send_ipc_message(message) ipc_server.send_ipc_message(message)
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...") if os.path.isdir(arg):
message = f"DIR|{arg}"
ipc_server.send_ipc_message(message)
return False
def ipc_realization_check(self, ipc_server): def ipc_realization_check(self, ipc_server):
try: try:
ipc_server.create_ipc_listener() ipc_server.create_ipc_listener()
except Exception: except (OSError, PermissionError) as e:
logger.info(f"IPC listener creation failed: {e}, falling back to test message")
ipc_server.send_test_ipc_message()
except Exception as e:
logger.error(f"Unexpected IPC setup error: {e}")
ipc_server.send_test_ipc_message() ipc_server.send_test_ipc_message()
try:
ipc_server.create_ipc_listener()
except Exception as e:
...
def setup_debug_hook(self): def setup_debug_hook(self):
try: # Typically: ValueError: signal only works in main thread
with suppress(ValueError):
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows # kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
signal.signal( signal.signal(
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"), vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
debug_signal_handler debug_signal_handler
) )
except ValueError:
# Typically: ValueError: signal only works in main thread
...

View File

@@ -1,3 +1,3 @@
""" """
Core Module Core Package
""" """

View File

@@ -1,3 +1,3 @@
""" """
Containers Module Containers Package
""" """

View File

@@ -8,6 +8,7 @@ from gi.repository import Gtk
# Application imports # Application imports
from .header_container import HeaderContainer from .header_container import HeaderContainer
from .body_container import BodyContainer from .body_container import BodyContainer
from .footer_container import FooterContainer
@@ -15,33 +16,34 @@ class BaseContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(BaseContainer, self).__init__() super(BaseContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show_all() self.show()
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL) self.ctx = self.get_style_context()
self.ctx.add_class("base-container") self.ctx.add_class("base-container")
self.set_orientation(Gtk.Orientation.VERTICAL)
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("update_transparency", self._update_transparency) event_system.subscribe("update-transparency", self._update_transparency)
event_system.subscribe("remove_transparency", self._remove_transparency) event_system.subscribe("remove-transparency", self._remove_transparency)
def _load_widgets(self): def _load_widgets(self):
self.add(HeaderContainer()) self.add( HeaderContainer() )
self.add(BodyContainer()) self.add( BodyContainer() )
self.add( FooterContainer() )
def _update_transparency(self): def _update_transparency(self):
self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}") self.ctx.add_class(f"mw_transparency_{settings_manager.settings.theming.transparency}")
def _remove_transparency(self): def _remove_transparency(self):
self.ctx.remove_class(f"mw_transparency_{settings.theming.transparency}") self.ctx.remove_class(f"mw_transparency_{settings_manager.settings.theming.transparency}")

View File

@@ -16,19 +16,19 @@ class BodyContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(BodyContainer, self).__init__() super(BodyContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show_all() self.show()
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL) self.ctx = self.get_style_context()
self.ctx.add_class("body-container") self.ctx.add_class("body-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_homogeneous(True) self.set_homogeneous(True)
def _setup_signals(self): def _setup_signals(self):
@@ -37,8 +37,7 @@ class BodyContainer(Gtk.Box):
def _subscribe_to_events(self): def _subscribe_to_events(self):
... ...
def _load_widgets(self): def _load_widgets(self):
self.add(LeftContainer()) self.add( LeftContainer() )
self.add(CenterContainer()) self.add( CenterContainer() )
self.add(RightContainer()) self.add( RightContainer() )

View File

@@ -14,32 +14,38 @@ class CenterContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(CenterContainer, self).__init__() super(CenterContainer, self).__init__()
self._builder = settings_manager.get_builder()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show()
def _setup_styling(self): def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("center-container")
self.set_orientation(Gtk.Orientation.VERTICAL) self.set_orientation(Gtk.Orientation.VERTICAL)
ctx = self.get_style_context() self.set_hexpand(True)
ctx.add_class("center-container") self.set_vexpand(True)
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
... ...
def _load_widgets(self): def _load_widgets(self):
glade_box = self._builder.get_object("glade_box") glade_box = widget_registery.get_object("glade_box")
button = Gtk.Button(label = "Click Me!") button = Gtk.Button(label = "Click Me!")
button.connect("clicked", self._hello_world) button.connect("clicked", self._hello_world)
button.show()
glade_box.show()
self.add(button) self.add(button)
self.add(glade_box) self.add(glade_box)
self.add( WebkitUI() ) self.add( WebkitUI() )

View File

@@ -0,0 +1,3 @@
"""
Containers Package
"""

View File

@@ -0,0 +1,64 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from ...widgets.code.code_base import CodeBase
from ...widgets.separator_widget import Separator
from ...widgets.code.mini_view_widget import MiniViewWidget
from .editors_container import EditorsContainer
class CodeContainer(Gtk.Box):
def __init__(self):
super(CodeContainer, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
code_base = CodeBase()
self.add( self._create_tabs_widgets(code_base) )
self.add( self._create_editor_widget(code_base) )
def _create_tabs_widgets(self, code_base: CodeBase):
scrolled_window = Gtk.ScrolledWindow()
viewport = Gtk.Viewport()
scrolled_window.set_overlay_scrolling(False)
viewport.add( code_base.get_tabs_widget() )
scrolled_window.add( viewport )
return scrolled_window
def _create_editor_widget(self, code_base: CodeBase):
editors_container = Gtk.Box()
editors_container.add( Separator("separator_left") )
editors_container.add( EditorsContainer(code_base) )
editors_container.add( Separator("separator_right") )
editors_container.add( code_base.get_mini_view_widget() )
return editors_container

View File

@@ -0,0 +1,71 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
# Application imports
class EditorsContainer(Gtk.Paned):
def __init__(self, code_base: any):
super(EditorsContainer, self).__init__()
self.code_base = code_base
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("paned-editors-container")
self.set_hexpand(True)
self.set_vexpand(True)
self.set_wide_handle(True)
def _setup_signals(self):
self.map_id = self.connect("map", self._init_map)
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.scrolled_win1, \
self.scrolled_win2 = self._create_views()
self.pack1( self.scrolled_win1, True, True )
self.pack2( self.scrolled_win2, True, True )
def _create_views(self):
scrolled_win1 = Gtk.ScrolledWindow()
scrolled_win2 = Gtk.ScrolledWindow()
source_view1 = self.code_base.create_source_view()
source_view2 = self.code_base.create_source_view()
source_view1.sibling_right = source_view2
source_view2.sibling_left = source_view1
scrolled_win1.add( source_view1 )
scrolled_win2.add( source_view2 )
return scrolled_win1, scrolled_win2
def _init_map(self, view):
def _first_show_init():
self.disconnect(self.map_id)
del self.map_id
self.code_base.first_map_load()
del self.code_base
return False
GLib.timeout_add(100, _first_show_init)

View File

@@ -0,0 +1,39 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from .code.code_container import CodeContainer
class FooterContainer(Gtk.Box):
def __init__(self):
super(FooterContainer, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("footer-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.add( CodeContainer() )

View File

@@ -6,7 +6,8 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from ..widgets.transparency_scale import TransparencyScale from ..widgets.controls.open_files_button import OpenFilesButton
from ..widgets.controls.transparency_scale import TransparencyScale
@@ -14,33 +15,37 @@ class HeaderContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(HeaderContainer, self).__init__() super(HeaderContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show_all() self.show()
def _setup_styling(self): def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL) self.ctx = self.get_style_context()
self.ctx.add_class("header-container") self.ctx.add_class("header-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
... event_system.subscribe("tggl-top-main-menubar", self.tggl_top_main_menubar)
def _load_widgets(self): def _load_widgets(self):
button = Gtk.Button(label = "Interactive Debug") button = Gtk.Button(label = "Interactive Debug")
button.connect("clicked", self._interactive_debug) button.connect("clicked", self._interactive_debug)
self.add(TransparencyScale()) self.add( OpenFilesButton() )
self.add( TransparencyScale() )
self.add(button) self.add(button)
def _interactive_debug(self, widget = None, eve = None): def _interactive_debug(self, widget = None, eve = None):
event_system.emit("load_interactive_debug") event_system.emit("load-interactive-debug")
def tggl_top_main_menubar(self):
self.hide() if self.is_visible() else self.show_all()

View File

@@ -18,17 +18,20 @@ class LeftContainer(Gtk.Box):
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show()
def _setup_styling(self): def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("left-container")
self.set_orientation(Gtk.Orientation.VERTICAL) self.set_orientation(Gtk.Orientation.VERTICAL)
ctx = self.get_style_context() self.set_vexpand(True)
ctx.add_class("left-container")
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
... ...
def _load_widgets(self): def _load_widgets(self):

View File

@@ -6,6 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from ..widgets.vte_widget import VteWidget
@@ -18,18 +19,22 @@ class RightContainer(Gtk.Box):
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets() self._load_widgets()
self.show()
def _setup_styling(self): def _setup_styling(self):
self.ctx = self.get_style_context()
self.ctx.add_class("right-container")
self.set_orientation(Gtk.Orientation.VERTICAL) self.set_orientation(Gtk.Orientation.VERTICAL)
ctx = self.get_style_context() self.set_vexpand(True)
ctx.add_class("right-container")
def _setup_signals(self): def _setup_signals(self):
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
... ...
def _load_widgets(self): def _load_widgets(self):
... vte_widget = VteWidget()
self.add( vte_widget )

View File

@@ -1,3 +1,3 @@
""" """
Controllers Module Controllers Package
""" """

View File

@@ -1,5 +1,4 @@
# Python imports # Python imports
import os
# Lib imports # Lib imports
import gi import gi
@@ -7,40 +6,48 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from plugins import plugins_controller
from libs.mixins.ipc_signals_mixin import IPCSignalsMixin from libs.mixins.ipc_signals_mixin import IPCSignalsMixin
from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin
from ..containers.base_container import BaseContainer from ..containers.base_container import BaseContainer
from .base_controller_data import BaseControllerData from .base_controller_mixin import BaseControllerMixin
from .bridge_controller import BridgeController from .bridge_controller import BridgeController
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData): class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerMixin):
def __init__(self, args, unknownargs): """ docstring for BaseController. """
self.setup_controller_data()
def __init__(self):
self._setup_controller_data()
self._load_plugins(is_pre = True)
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_controllers() self._load_controllers()
self._load_plugins(is_pre = False)
if args.no_plugins == "false": self._load_files()
self.plugins.launch_plugins()
for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg):
message = f"FILE|{arg}"
event_system.emit("post_file_to_ipc", message)
if os.path.isdir(arg):
message = f"DIR|{arg}"
event_system.emit("post_file_to_ipc", message)
logger.info(f"Made it past {self.__class__} loading...") logger.info(f"Made it past {self.__class__} loading...")
settings_manager.set_end_load_time()
settings_manager.log_load_time()
def _setup_controller_data(self):
self.window = settings_manager.get_main_window()
self.base_container = BaseContainer()
self.plugins_controller = plugins_controller
widget_registery.expose_object("main_window", self.window)
settings_manager.register_signals_to_builder([self, self.base_container])
self._collect_files_dirs()
def _setup_styling(self): def _setup_styling(self):
... ...
@@ -50,23 +57,30 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
self.window.connect("key-release-event", self.on_global_key_release_controller) self.window.connect("key-release-event", self.on_global_key_release_controller)
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("shutting_down", lambda: print("Shutting down...")) event_system.subscribe("shutting-down", lambda: print("Shutting down..."))
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) event_system.subscribe("handle-file-from-ipc", self.handle_file_from_ipc)
event_system.subscribe("handle_dir_from_ipc", self.handle_dir_from_ipc) event_system.subscribe("handle-dir-from-ipc", self.handle_dir_from_ipc)
event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar) event_system.subscribe("tggl-top-main-menubar", self._tggl_top_main_menubar)
def _load_controllers(self): def _load_controllers(self):
BridgeController() BridgeController()
def _load_plugins(self, is_pre: bool):
args, unknownargs = settings_manager.get_starting_args()
if args.no_plugins == "true": return
if is_pre:
self.plugins_controller.pre_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)
def _tggl_top_main_menubar(self): def _tggl_top_main_menubar(self):
logger.debug("_tggl_top_main_menubar > stub...") logger.debug("_tggl_top_main_menubar > stub...")
def setup_builder_and_container(self):
self.builder = Gtk.Builder()
self.builder.add_from_file(settings_manager.get_glade_file())
self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder)
self.base_container = BaseContainer()
settings_manager.register_signals_to_builder([self, self.base_container])

View File

@@ -6,24 +6,33 @@ from shutil import which
# Lib imports # Lib imports
# Application imports # Application imports
from plugins.plugins_controller import PluginsController
class BaseControllerData: class BaseControllerMixin:
''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. ''' ''' BaseControllerMixin contains most of the state of the app at ay given time. It also has some support methods. '''
def setup_controller_data(self) -> None: def _collect_files_dirs(self):
self.window = settings_manager.get_main_window() args, \
self.builder = None unknownargs = settings_manager.get_starting_args()
self.base_container = None files = []
self.was_midified_key = False
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
self.setup_builder_and_container() for arg in unknownargs + [args.new_tab,]:
self.plugins = PluginsController() if os.path.isdir( arg.replace("file://", "") ):
files.append( f"DIR|{arg.replace('file://', '')}" )
continue
# NOTE: If passing line number with file split against :
if os.path.isfile( arg.replace("file://", "").split(":")[0] ):
files.append( f"FILE|{arg.replace('file://', '')}" )
continue
logger.info(f"Not a File: {arg}")
if len(files) == 0: return
settings_manager.set_is_starting_with_file(True)
settings_manager.set_starting_files(files)
def get_base_container(self): def get_base_container(self):
return self.base_container return self.base_container
@@ -59,24 +68,22 @@ class BaseControllerData:
widget.remove(child) widget.remove(child)
def get_clipboard_data(self, encoding = "utf-8") -> str: def get_clipboard_data(self, encoding = "utf-8") -> str:
if which("xclip"): if not which("xclip"):
command = ['xclip','-selection','clipboard']
else:
logger.info('xclip not found...') logger.info('xclip not found...')
return return
command = ['xclip','-selection','clipboard']
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout = subprocess.PIPE) proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout = subprocess.PIPE)
retcode = proc.wait() retcode = proc.wait()
data = proc.stdout.read() data = proc.stdout.read()
return data.decode(encoding).strip() return data.decode(encoding).strip()
def set_clipboard_data(self, data: type, encoding = "utf-8") -> None: def set_clipboard_data(self, data: type, encoding = "utf-8") -> None:
if which("xclip"): if not which("xclip"):
command = ['xclip','-selection','clipboard']
else:
logger.info('xclip not found...') logger.info('xclip not found...')
return return
command = ['xclip','-selection','clipboard']
proc = subprocess.Popen(command, stdin = subprocess.PIPE) proc = subprocess.Popen(command, stdin = subprocess.PIPE)
proc.stdin.write(data.encode(encoding)) proc.stdin.write(data.encode(encoding))
proc.stdin.close() proc.stdin.close()

View File

@@ -10,8 +10,6 @@ import base64
class BridgeController: class BridgeController:
def __init__(self): def __init__(self):
self.opened_files = {}
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@@ -20,19 +18,19 @@ class BridgeController:
... ...
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("handle_bridge_event", self.handle_bridge_event) event_system.subscribe("handle-bridge-event", self.handle_bridge_event)
def handle_bridge_event(self, event): def handle_bridge_event(self, event):
match event.topic: match event.topic:
case "save": case "save":
event_system.emit(f"handle_file_event_{event.originator}", (event,)) event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "close": case "close":
event_system.emit(f"handle_file_event_{event.originator}", (event,)) event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "load_buffer": case "load_buffer":
event_system.emit(f"handle_file_event_{event.originator}", (event,)) event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "load_file": case "load_file":
event_system.emit(f"handle_file_event_{event.originator}", (event,)) event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "alert": case "alert":
content = base64.b64decode( event.content.encode() ).decode("utf-8") content = base64.b64decode( event.content.encode() ).decode("utf-8")
logger.info(f"\nMessage Topic: {event.topic}\nMessage Content: {content}") logger.info(f"\nMessage Topic: {event.topic}\nMessage Content: {content}")

View File

@@ -1,3 +1,3 @@
""" """
Widgets Module Widgets Package
""" """

View File

@@ -0,0 +1,110 @@
# Python imports
from datetime import datetime
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GObject
# Application imports
class CalendarWidget(Gtk.Popover):
def __init__(self):
super(CalendarWidget, 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):
...
def _load_widgets(self):
self.body = Gtk.Calendar()
self.body.show()
self.add(self.body)
class ClockWidget(Gtk.EventBox):
def __init__(self):
super(ClockWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_size_request(180, -1)
ctx = self.get_style_context()
ctx.add_class("clock-widget")
def _setup_signals(self):
self.connect("button_release_event", self._toggle_cal_popover)
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.calendar = CalendarWidget()
self.label = Gtk.Label()
self.calendar.set_relative_to(self)
self.label.set_justify(Gtk.Justification.CENTER)
self.label.set_margin_top(15)
self.label.set_margin_bottom(15)
self.label.set_margin_left(15)
self.label.set_margin_right(15)
self._update_face()
self.add(self.label)
GObject.timeout_add(59000, self._update_face)
def _update_face(self):
dt_now = datetime.now()
hours_mins_sec = dt_now.strftime("%I:%M %p")
month_day_year = dt_now.strftime("%m/%d/%Y")
time_str = hours_mins_sec + "\n" + month_day_year
self.label.set_label(time_str)
def _toggle_cal_popover(self, widget, eve):
if (self.calendar.get_visible() == True):
self.calendar.popdown()
return
now = datetime.now()
timeStr = now.strftime("%m/%d/%Y")
parts = timeStr.split("/")
month = int(parts[0]) - 1
day = int(parts[1])
year = int(parts[2])
self.calendar.body.select_day(day)
self.calendar.body.select_month(month, year)
self.calendar.popup()

View File

@@ -0,0 +1,3 @@
"""
Code Package
"""

View File

@@ -0,0 +1,61 @@
# Python imports
# Lib imports
# Application imports
from plugins import plugins_controller
from libs.controllers.controller_manager import ControllerManager
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.views.source_views_controller import SourceViewsController
from .mini_view_widget import MiniViewWidget
class CodeBase:
def __init__(self):
super(CodeBase, self).__init__()
self.controller_manager: ControllerManager = ControllerManager()
self.miniview_widget: MiniViewWidget = MiniViewWidget()
self._load_controllers()
def _load_controllers(self):
files_controller = FilesController()
tabs_controller = TabsController()
commands_controller = CommandsController()
completion_controller = CompletionController()
source_views_controller = SourceViewsController()
# self.controller_manager.register_controller("base", self)
self.controller_manager.register_controller("files", files_controller)
self.controller_manager.register_controller("tabs", tabs_controller)
self.controller_manager.register_controller("commands", commands_controller)
self.controller_manager.register_controller("completion", completion_controller)
self.controller_manager.register_controller("source_views", source_views_controller)
self.controller_manager.register_controller("plugins", plugins_controller)
self.controller_manager.register_controller("widgets", widget_registery)
def get_tabs_widget(self):
return self.controller_manager["tabs"].get_tabs_widget()
def get_mini_view_widget(self):
return self.miniview_widget
def create_source_view(self):
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()

View File

@@ -0,0 +1,5 @@
"""
Code Command System Package
"""
from .command_system import CommandSystem

View File

@@ -0,0 +1,27 @@
# Python imports
# Lib imports
from gi.repository import GtkSource
# Application imports
def set_language_and_style(view, file):
language = view.language_manager.guess_language(file.fname, None)
file.ftype = "buffer" if not language else language
file.buffer.set_language(language)
file.buffer.set_style_scheme(view.syntax_theme)
return language
def update_info_bar_if_focused(command_system, view: GtkSource):
has_focus = command_system.exec("has_focus")
if has_focus:
command_system.exec("update_info_bar")
def get_file_and_buffer(view: GtkSource):
file = view.command.get_file(view)
buffer = file.buffer
return file, buffer

View File

@@ -0,0 +1,97 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from ..source_view import SourceView
from . import commands
class CommandSystem:
def __init__(self):
super(CommandSystem, self).__init__()
self.data: list = ()
def set_data(self, *args, **kwargs):
self.data = (args, kwargs)
def exec(self, command: str) -> any:
if not hasattr(commands, command): return
method = getattr(commands, command)
args, kwargs = self.data
if kwargs:
return method.execute(*args, kwargs)
else:
return method.execute(*args)
def exec_with_args(self, command: str, args: list) -> any:
if not hasattr(commands, command): return
method = getattr(commands, command)
return method.execute(*args)
def emit(self, event: Code_Event_Types.CodeEvent):
""" Monkey patch 'emit' from command controller... """
...
def emit_to(self, controller: str, event: Code_Event_Types.CodeEvent):
""" Monkey patch 'emit_to' from command controller... """
...
def get_file(self, view: SourceView):
event = Event_Factory.create_event(
"get_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def get_swap_file(self, view: SourceView):
event = Event_Factory.create_event(
"get_swap_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def new_file(self, view: SourceView):
event = Event_Factory.create_event("add_new_file", view = view)
self.emit_to("files", event)
return event.response
def remove_file(self, view: SourceView):
event = Event_Factory.create_event(
"removed_file",
view = view,
buffer = view.get_buffer()
)
self.emit_to("files", event)
return event.response
def request_completion(self, view: SourceView):
event = Event_Factory.create_event(
"request_completion",
view = view,
buffer = view.get_buffer()
)
self.emit_to("completion", event)

View File

@@ -0,0 +1,16 @@
"""
Code Commands Package
"""
import pkgutil
import importlib
__all__ = []
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
module = importlib.import_module(f"{__name__}.{module_name}")
globals()[module_name] = module # Add module to package namespace
__all__.append(module_name)
del pkgutil
del importlib

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Buffer Redo")
buffer = view.get_buffer()
undo_manager = buffer.get_undo_manager()
if undo_manager.can_redo():
buffer.redo()

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Buffer Undo")
buffer = view.get_buffer()
undo_manager = buffer.get_undo_manager()
if undo_manager.can_undo():
buffer.undo()

View File

@@ -0,0 +1,20 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Close File")
view.command.remove_file(view)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,36 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Cut to Temp Buffer")
view.clear_temp_cut_buffer_delayed()
buffer = view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
start_itr = itr.copy()
end_itr = itr.copy()
start_line = itr.get_line() + 1
start_char = itr.get_line_offset()
start_itr.backward_visible_line()
start_itr.forward_line()
end_itr.forward_line()
line_str = buffer.get_slice(start_itr, end_itr, True)
view._cut_buffer += f"{line_str}"
buffer.delete(start_itr, end_itr)
view.set_temp_cut_buffer_delayed()

View File

@@ -0,0 +1,34 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View,
uri: str
):
logger.debug("Command: DnD Load File To Buffer")
file = view.command.get_file(view)
buffer = file.buffer
if not file.ftype == "buffer":
file = view.command.new_file(view)
gfile = Gio.File.new_for_uri(uri)
view.command.exec_with_args(
"load_file",
(view, gfile, file)
)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
def execute(
view: GtkSource.View,
uris: list = []
):
logger.debug("Command: DnD Load Files")
for uri in uris:
try:
gfile = Gio.File.new_for_uri(uri)
except Exception as e:
gfile = Gio.File.new_for_path(uri)
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,53 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Duplicate Line")
buffer = view.get_buffer()
if not buffer.get_has_selection():
had_selection = False
itr = buffer.get_iter_at_mark( buffer.get_insert() )
start_itr = itr.copy()
end_itr = itr.copy()
start_line = itr.get_line() + 1
start_char = itr.get_line_offset()
else:
had_selection = True
start_itr, end_itr = buffer.get_selection_bounds()
sline = start_itr.get_line()
eline = end_itr.get_line()
start_line = eline + 1
start_char = start_itr.get_line_offset()
end_char = end_itr.get_line_offset()
range_line_size = eline - sline
start_itr.backward_visible_line()
start_itr.forward_line()
end_itr.forward_line()
end_itr.backward_char()
line_str = buffer.get_slice(start_itr, end_itr, True)
end_itr.forward_char()
buffer.insert(end_itr, f"{line_str}\n", -1)
if not had_selection:
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
buffer.place_cursor(new_itr)
else:
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
new_end_itr = buffer.get_iter_at_line_offset((start_line + range_line_size), end_char)
buffer.select_range(new_itr, new_end_itr)

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Focus Left Sibling")
if not view.sibling_left: return
view.sibling_left.grab_focus()

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Focus Right Sibling")
if not view.sibling_right: return
view.sibling_right.grab_focus()

View File

@@ -0,0 +1,21 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get Current File")
file = view.command.get_file(view)
return file

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get File Type")
file = view.command.get_file(view)
return file.ftype

View File

@@ -0,0 +1,21 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Get Text")
buffer = view.get_buffer()
start_itr, end_itr = buffer.get_bounds()
return buffer.get_text(start_itr, end_itr, True)

View File

@@ -0,0 +1,31 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Go-To")
file = view.command.get_file(view)
gfile = file.get_location()
uri = gfile.get_uri()
buffer = view.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line()
offset = iter.get_line_offset()
event_system.emit(
"textDocument/definition",
(view, file.ftype, uri, line, offset,)
)

View File

@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Has Focus")
ctx = view.get_parent().get_style_context()
return ctx.has_class("source-view-focused")

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Line Down")
view.emit("move-lines", True)

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Line Up")
view.emit("move-lines", False)

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
from ...source_file import SourceFile
from ..command_helpers import set_language_and_style
def execute(
view: GtkSource.View,
gfile: Gio.File,
file: SourceFile = None,
):
logger.debug("Command: Load File")
if not file:
file = view.command.new_file(view)
file.load_path(gfile)
set_language_and_style(view, file)

View File

@@ -0,0 +1,40 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import Gio
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Load Start File(s)")
starting_files = settings_manager.get_starting_files()
if len(starting_files) == 0: return
file = starting_files.pop()
file = file.replace("FILE|", "")
gfile = Gio.File.new_for_path(file)
file = view.command.get_file(view)
view.command.exec_with_args(
"load_file",
(view, gfile, file)
)
if len(starting_files) == 0: return
for file in starting_files:
file = file.replace("FILE|", "")
gfile = Gio.File.new_for_path(file)
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,29 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Move To Left Sibling")
if not view.sibling_left: return
buffer = view.get_buffer()
popped_file, next_file = view.command.get_swap_file(view)
view.sibling_left.set_buffer(buffer)
view.sibling_left.grab_focus()
if next_file:
view.set_buffer(next_file.buffer)
else:
view.command.exec("new_file")

View File

@@ -0,0 +1,29 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Move To Right Sibling")
if not view.sibling_right: return
buffer = view.get_buffer()
popped_file, next_file = view.command.get_swap_file(view)
view.sibling_right.set_buffer(buffer)
view.sibling_right.grab_focus()
if next_file:
view.set_buffer(next_file.buffer)
else:
view.command.exec("new_file")

View File

@@ -0,0 +1,27 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: New File")
file = view.command.new_file(view)
set_language_and_style(view, file)
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)
return file

View File

@@ -0,0 +1,30 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Open File(s)")
gfiles = event_system.emit_and_await("open-files")
if not gfiles: return
file = view.command.get_file(view)
if file.ftype == "buffer":
gfile = gfiles.pop()
view.command.exec_with_args("load_file", (view, gfile, file))
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)
for i, gfile in enumerate(gfiles):
view.command.exec_with_args("load_file", (view, gfile))

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GLib
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Paste Temp Buffer")
view.clear_temp_cut_buffer_delayed()
buffer = view.get_buffer()
itr = buffer.get_iter_at_mark( buffer.get_insert() )
insert_itr = itr.copy()
buffer.insert(insert_itr, view._cut_buffer, -1)
view.set_temp_cut_buffer_delayed()

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Save File")
file = view.command.get_file(view)
buffer = file.buffer
if file.ftype == "buffer":
file.save_as()
set_language_and_style(view, file)
return
file.save()

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..command_helpers import set_language_and_style, update_info_bar_if_focused
def execute(
view: GtkSource.View = None
):
logger.info("Command: Save File As")
file = view.command.get_file(view)
buffer = file.buffer
file.save_as()
set_language_and_style(view, file)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,30 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ...source_file import SourceFile
from ..command_helpers import update_info_bar_if_focused
def execute(
view: GtkSource.View,
file: SourceFile
):
logger.debug("Command: Set Buffer")
if not file:
view.command.new_file(view)
return
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
language: str
):
logger.debug("Command: Set Buffer Language")
buffer = view.get_buffer()
buffer.set_language(
view.language_manager.get_language(language)
)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
style: str
):
logger.debug("Command: Set Buffer Style")
buffer = view.get_buffer()
buffer.set_style_scheme(
view.style_scheme_manager.get_scheme(style)
)

View File

@@ -0,0 +1,26 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Set Focus Border")
ctx = view.get_parent().get_style_context()
ctx.add_class("source-view-focused")
if view.sibling_right:
ctx = view.sibling_right.get_parent().get_style_context()
elif view.sibling_left:
ctx = view.sibling_left.get_parent().get_style_context()
ctx.remove_class("source-view-focused")

View File

@@ -0,0 +1,18 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Set MiniView")
event_system.emit("set-mini-view", (view,))

View File

@@ -0,0 +1,28 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Show Completion")
completer = view.get_completion()
providers = completer.get_providers()
if not providers:
view.command.request_completion(view)
return
completer.start(
providers,
completer.create_context()
)

View File

@@ -0,0 +1,31 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View,
):
logger.debug("Command: Update Info Bar")
file = view.command.get_file(view)
buffer = file.buffer
if not file: return
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() + 1
column = iter.get_line_offset()
ftype = file.ftype.get_id() if hasattr(file.ftype, "get_id") else file.ftype
event_system.emit(
"set-info-labels",
(file.fpath, f"{line}:{column}", ftype, file.encoding)
)

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Zoom In")
ctx = view.get_style_context()
if view.zoom_level < 99:
ctx.remove_class(f"px{view.zoom_level}")
view.zoom_level += 1
ctx.add_class(f"px{view.zoom_level}")

View File

@@ -0,0 +1,23 @@
# Python imports
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
def execute(
view: GtkSource.View = None
):
logger.debug("Command: Zoom Out")
ctx = view.get_style_context()
if view.zoom_level > 1:
ctx.remove_class(f"px{view.zoom_level}")
view.zoom_level -= 1
ctx.add_class(f"px{view.zoom_level}")

View File

@@ -0,0 +1,3 @@
"""
Code Completion Providers Package
"""

Some files were not shown because too many files have changed in this diff Show More