Moved completers to new dir; improved completers and added word completion
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
@@ -20,14 +20,14 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
__gtype_name__ = 'ExampleCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
GObject.Object.__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 Completion'
|
||||
return 'Example Code Completion'
|
||||
|
||||
def do_match(self, context):
|
||||
# word = context.get_word()
|
||||
@@ -43,6 +43,15 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
""" 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
|
||||
@@ -51,7 +60,17 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
proposals = self.response_cache.filter_with_context(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)
|
||||
|
||||
@@ -50,10 +50,10 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str):
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
...
|
||||
|
||||
def filter_with_context(self, context):
|
||||
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)
|
||||
@@ -64,12 +64,12 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
|
||||
"""
|
||||
|
||||
proposals = [
|
||||
self.create_completion_item(
|
||||
self.matchers[ "hello" ]["label"],
|
||||
self.matchers[ "hello" ]["text"],
|
||||
self.matchers[ "hello" ]["info"]
|
||||
)
|
||||
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...
|
||||
@@ -90,21 +90,20 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
if re.match(r'.*\{\{\s*custom\.$', left_text):
|
||||
# optionally proposed based on left search via regex
|
||||
proposals.append(
|
||||
self.create_completion_item(
|
||||
self.matchers[ "foo" ]["label"],
|
||||
self.matchers[ "foo" ]["text"]
|
||||
)
|
||||
{
|
||||
"label": self.matchers[ "foo" ]["label"],
|
||||
"text": self.matchers[ "foo" ]["text"],
|
||||
"info": ""
|
||||
}
|
||||
)
|
||||
|
||||
# optionally proposed based on left search via regex
|
||||
proposals.append(
|
||||
self.create_completion_item(
|
||||
self.matchers[ "bar" ]["label"],
|
||||
self.matchers[ "bar" ]["text"]
|
||||
)
|
||||
{
|
||||
"label": self.matchers[ "bar" ]["label"],
|
||||
"text": self.matchers[ "bar" ]["text"],
|
||||
"info": ""
|
||||
}
|
||||
)
|
||||
|
||||
return proposals
|
||||
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
class Provider(GObject.Object, GtkSource.CompletionProvider):
|
||||
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
|
||||
@@ -20,7 +20,7 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
|
||||
__gtype_name__ = 'LSPProvider'
|
||||
|
||||
def __init__(self):
|
||||
GObject.Object.__init__(self)
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
@@ -31,14 +31,11 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
|
||||
def do_get_name(self):
|
||||
return "LSP Code Completion"
|
||||
|
||||
def get_iter_correctly(self, context):
|
||||
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
|
||||
|
||||
def do_match(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
iter = self.get_iter_correctly(context)
|
||||
iter = 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
|
||||
@@ -56,6 +53,13 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
|
||||
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
|
||||
@@ -63,17 +67,16 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
|
||||
# return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
proposals = self.get_completion_filter(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)
|
||||
|
||||
def get_completion_filter(self, context):
|
||||
proposals = [
|
||||
self.response_cache.create_completion_item(
|
||||
"LSP Class",
|
||||
"LSP Code",
|
||||
"A test LSP completion item..."
|
||||
)
|
||||
]
|
||||
|
||||
return proposals
|
||||
@@ -30,16 +30,16 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str):
|
||||
...
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
return []
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext):
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
proposals = [
|
||||
self.create_completion_item(
|
||||
"LSP Class",
|
||||
"LSP Code",
|
||||
"A test LSP completion item..."
|
||||
)
|
||||
{
|
||||
"label": "LSP Class",
|
||||
"text": "LSP Code",
|
||||
"info": "A test LSP completion item..."
|
||||
}
|
||||
]
|
||||
|
||||
return proposals
|
||||
80
plugins/completers/python_completer/provider.py
Normal file
80
plugins/completers/python_completer/provider.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# 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 A python code completion plugin for Newton.
|
||||
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
|
||||
"""
|
||||
__gtype_name__ = 'PythonCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return "Python 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.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)
|
||||
106
plugins/completers/python_completer/provider_response_cache.py
Normal file
106
plugins/completers/python_completer/provider_response_cache.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
import jedi
|
||||
from jedi.api import Script
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
# FIXME: Find real icon names...
|
||||
icon_names = {
|
||||
'import': '',
|
||||
'module': '',
|
||||
'class': '',
|
||||
'function': '',
|
||||
'statement': '',
|
||||
'param': ''
|
||||
}
|
||||
|
||||
|
||||
class Jedi:
|
||||
def get_script(file, doc_text):
|
||||
return Script(code = doc_text, path = file)
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self._file = None
|
||||
|
||||
|
||||
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] = []
|
||||
|
||||
return response
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
if not self._file: return response
|
||||
|
||||
itr = self.get_iter_correctly(context)
|
||||
buffer = itr.get_buffer()
|
||||
|
||||
doc_text = buffer.get_text(
|
||||
buffer.get_start_iter(),
|
||||
buffer.get_end_iter(),
|
||||
False
|
||||
)
|
||||
iter_cursor = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
linenum = iter_cursor.get_line() + 1
|
||||
charnum = iter_cursor.get_line_index()
|
||||
|
||||
def create_generator():
|
||||
if not self._file: return []
|
||||
|
||||
for completion in Jedi.get_script(self._file, doc_text).complete(
|
||||
line = linenum,
|
||||
column = None,
|
||||
fuzzy = False
|
||||
):
|
||||
{
|
||||
"label": completion.name,
|
||||
"text": completion.name,
|
||||
"info": completion.docstring()
|
||||
"icon" self.get_icon_for_type(completion.type)
|
||||
}
|
||||
|
||||
yield comp_item
|
||||
|
||||
for item in create_generator():
|
||||
response.append(item)
|
||||
|
||||
return response
|
||||
|
||||
def get_icon_for_type(self, _type):
|
||||
try:
|
||||
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
|
||||
except (KeyError, AttributeError, GObject.GError) as e:
|
||||
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
|
||||
except (GObject.GError, AttributeError) as e:
|
||||
return None
|
||||
@@ -3,11 +3,9 @@ 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
|
||||
@@ -23,7 +21,7 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
__gtype_name__ = 'SnippetsCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
GObject.Object.__init__(self)
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
@@ -40,6 +38,13 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
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
|
||||
@@ -52,8 +52,8 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str):
|
||||
response: list = []
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
for entry in self.matchers:
|
||||
if not word in entry: continue
|
||||
@@ -62,5 +62,7 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
|
||||
return response
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext):
|
||||
...
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
return response
|
||||
3
plugins/completers/words_completer/__init__.py
Normal file
3
plugins/completers/words_completer/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
3
plugins/completers/words_completer/__main__.py
Normal file
3
plugins/completers/words_completer/__main__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
53
plugins/completers/words_completer/alt_provider.py
Normal file
53
plugins/completers/words_completer/alt_provider.py
Normal 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
|
||||
7
plugins/completers/words_completer/manifest.json
Normal file
7
plugins/completers/words_completer/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Words Completer",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
40
plugins/completers/words_completer/plugin.py
Normal file
40
plugins/completers/words_completer/plugin.py
Normal 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):
|
||||
...
|
||||
71
plugins/completers/words_completer/provider.py
Normal file
71
plugins/completers/words_completer/provider.py
Normal 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)
|
||||
131
plugins/completers/words_completer/provider_response_cache.py
Normal file
131
plugins/completers/words_completer/provider_response_cache.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
self.load_as_new_set(event.file.buffer)
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
self.matchers[event.file.buffer] = []
|
||||
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
|
||||
# if self.get_if_in_matched_word_set(buffer): return
|
||||
self.load_as_new_set(buffer)
|
||||
|
||||
|
||||
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_as_new_set(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:
|
||||
self.matchers[buffer] = set()
|
||||
return
|
||||
|
||||
self.matchers[buffer] = self.get_all_words(data)
|
||||
|
||||
def get_if_in_matched_word_set(self, buffer):
|
||||
was_found = False
|
||||
|
||||
if not buffer in self.matchers: return was_found
|
||||
|
||||
insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
end_itr = insert_itr.copy()
|
||||
start_itr = end_itr.copy()
|
||||
|
||||
if not start_itr.starts_word():
|
||||
start_itr.backward_word_start()
|
||||
|
||||
if not end_itr.ends_word():
|
||||
end_itr.forward_word_end()
|
||||
|
||||
word = buffer.get_text(start_itr, end_itr, False)
|
||||
for _word in self.matchers[buffer]:
|
||||
if not _word.startswith(word): continue
|
||||
was_found = True
|
||||
|
||||
if was_found: return was_found
|
||||
|
||||
self.matchers[buffer].add(word)
|
||||
|
||||
return was_found
|
||||
|
||||
|
||||
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
|
||||
@@ -1,128 +0,0 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
|
||||
import jedi
|
||||
from jedi.api import Script
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
# FIXME: Find real icon names...
|
||||
icon_names = {
|
||||
'import': '',
|
||||
'module': '',
|
||||
'class': '',
|
||||
'function': '',
|
||||
'statement': '',
|
||||
'param': ''
|
||||
}
|
||||
|
||||
|
||||
class Jedi:
|
||||
def get_script(file, doc_text):
|
||||
return Script(code = doc_text, path = file)
|
||||
|
||||
|
||||
class PythonCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This code is A python code completion plugin for Newton.
|
||||
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
|
||||
"""
|
||||
__gtype_name__ = 'PythonProvider'
|
||||
|
||||
def __init__(self):
|
||||
GObject.Object.__init__(self)
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
self._theme = Gtk.IconTheme.get_default()
|
||||
self._file = None
|
||||
|
||||
def do_get_name(self):
|
||||
return "Python Code Completion"
|
||||
|
||||
def get_iter_correctly(self, context):
|
||||
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
|
||||
|
||||
def do_match(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
iter = self.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
|
||||
ch = iter.get_char()
|
||||
# NOTE: Look to re-add or apply supprting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
buffer = iter.get_buffer()
|
||||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 5
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
# Note: Filtering needs to happen before getting here in some type of symbol cache
|
||||
# TODO: Maybe convert async?
|
||||
if not self._file: return
|
||||
|
||||
it = self.get_iter_correctly(context)
|
||||
buffer = it.get_buffer()
|
||||
proposals = []
|
||||
|
||||
doc_text = buffer.get_text(
|
||||
buffer.get_start_iter(),
|
||||
buffer.get_end_iter(),
|
||||
False
|
||||
)
|
||||
iter_cursor = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
linenum = iter_cursor.get_line() + 1
|
||||
charnum = iter_cursor.get_line_index()
|
||||
|
||||
def create_generator():
|
||||
if not self._file: return
|
||||
|
||||
for completion in Jedi.get_script(self._file, doc_text).complete(
|
||||
line = linenum,
|
||||
column = None,
|
||||
fuzzy = False
|
||||
):
|
||||
comp_item = GtkSource.CompletionItem.new()
|
||||
comp_item.set_label(completion.name)
|
||||
comp_item.set_text(completion.name)
|
||||
comp_item.set_icon(self.get_icon_for_type(completion.type))
|
||||
comp_item.set_info(completion.docstring())
|
||||
|
||||
yield comp_item
|
||||
|
||||
for item in create_generator():
|
||||
proposals.append(item)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
|
||||
def get_icon_for_type(self, _type):
|
||||
try:
|
||||
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
|
||||
except (KeyError, AttributeError, GObject.GError) as e:
|
||||
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
|
||||
except (GObject.GError, AttributeError) as e:
|
||||
return None
|
||||
@@ -1,37 +0,0 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
...
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str):
|
||||
...
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext):
|
||||
...
|
||||
@@ -38,20 +38,20 @@ class ProviderResponseCacheBase:
|
||||
def process_file_change(self, buffer: GtkSource.Buffer):
|
||||
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...")
|
||||
|
||||
def filter(self, word: str):
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter' not implemented...")
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext):
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
|
||||
|
||||
|
||||
def create_completion_item(
|
||||
self,
|
||||
label: str = "",
|
||||
text: str = "",
|
||||
info: str = "",
|
||||
completion: any = None
|
||||
):
|
||||
label: str = "",
|
||||
text: str = "",
|
||||
info: str = "",
|
||||
icon: any = None
|
||||
) -> dict:
|
||||
if not label or not text: return
|
||||
|
||||
comp_item = GtkSource.CompletionItem.new()
|
||||
@@ -62,26 +62,58 @@ class ProviderResponseCacheBase:
|
||||
comp_item.set_info(info)
|
||||
# comp_item.set_markup(f"<h3>{info}</h3>")
|
||||
|
||||
if completion:
|
||||
if icon:
|
||||
comp_item.set_icon(
|
||||
self.get_icon_for_type(completion.type)
|
||||
self.get_icon_for_type(icon.type)
|
||||
)
|
||||
|
||||
return comp_item
|
||||
|
||||
def get_word(self, context):
|
||||
start_iter = context.get_iter()
|
||||
end_iter = None
|
||||
def get_all_marks(self, buffer) -> list:
|
||||
marks: list = []
|
||||
iter_ = buffer.get_start_iter()
|
||||
|
||||
if not isinstance(start_iter, Gtk.TextIter):
|
||||
_, start_iter = context.get_iter()
|
||||
end_iter = start_iter.copy()
|
||||
while iter_:
|
||||
marks = iter_.get_marks()
|
||||
|
||||
for mark in marks:
|
||||
if mark and mark not in marks:
|
||||
marks.append(mark)
|
||||
|
||||
if not iter_.forward_char():
|
||||
break
|
||||
|
||||
return marks
|
||||
|
||||
def get_all_insert_marks(self, buffer) -> list:
|
||||
marks: list = []
|
||||
iter_ = buffer.get_start_iter()
|
||||
|
||||
while iter_:
|
||||
marks = iter_.get_marks()
|
||||
|
||||
for mark in marks:
|
||||
if mark.get_name() and "multi_insert_" in mark.get_name():
|
||||
marks.append(mark)
|
||||
|
||||
if not iter_.forward_char():
|
||||
break
|
||||
|
||||
return marks
|
||||
|
||||
def get_word(self, context) -> str:
|
||||
start_iter = self.get_iter_correctly(context)
|
||||
end_iter = start_iter.copy()
|
||||
|
||||
if not start_iter.starts_word():
|
||||
start_iter.backward_word_start()
|
||||
|
||||
end_iter.forward_word_end()
|
||||
if not end_iter.ends_word():
|
||||
end_iter.forward_word_end()
|
||||
|
||||
buffer = start_iter.get_buffer()
|
||||
|
||||
return buffer.get_text(start_iter, end_iter, False)
|
||||
|
||||
def get_iter_correctly(self, context) -> Gtk.TextIter:
|
||||
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
|
||||
|
||||
@@ -17,9 +17,6 @@ class CompletionController(ControllerBase):
|
||||
def __init__(self):
|
||||
super(CompletionController, self).__init__()
|
||||
|
||||
self.words_provider = GtkSource.CompletionWords.new("words", None)
|
||||
self.words_provider.props.activation = GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
self._completers: list[GtkSource.Completion] = []
|
||||
self._providers: dict[str, GtkSource.CompletionProvider] = {}
|
||||
|
||||
@@ -45,10 +42,12 @@ class CompletionController(ControllerBase):
|
||||
def register_completer(self, completer: GtkSource.Completion):
|
||||
self._completers.append(completer)
|
||||
|
||||
completer.add_provider(self.words_provider)
|
||||
for provider in self._providers.values():
|
||||
completer.add_provider(provider)
|
||||
|
||||
def unregister_completer(self, completer: GtkSource.Completion):
|
||||
self._completers.remove(completer)
|
||||
|
||||
def register_provider(
|
||||
self,
|
||||
provider_name: str,
|
||||
@@ -68,14 +67,10 @@ class CompletionController(ControllerBase):
|
||||
completer.remove_provider(provider)
|
||||
|
||||
def provider_process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
self.words_provider.register(event.file.buffer)
|
||||
|
||||
for provider in self._providers.values():
|
||||
provider.response_cache.process_file_load(event)
|
||||
|
||||
def provider_process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
self.words_provider.unregister(event.file.buffer)
|
||||
|
||||
for provider in self._providers.values():
|
||||
provider.response_cache.process_file_close(event)
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ class SourceViewsMultiInsertState(MarkEventsMixin):
|
||||
if not self.insert_markers: return False
|
||||
|
||||
buffer = file.buffer
|
||||
if buffer.is_processing_completion:
|
||||
self.insert_completion_text(buffer, text)
|
||||
return True
|
||||
|
||||
# freeze buffer and insert to each mark (if any)
|
||||
buffer.block_insert_after_signal()
|
||||
@@ -49,6 +52,32 @@ class SourceViewsMultiInsertState(MarkEventsMixin):
|
||||
|
||||
return True
|
||||
|
||||
def insert_completion_text(self, buffer, text):
|
||||
buffer.is_processing_completion = False
|
||||
|
||||
# freeze buffer and insert to each mark (if any)
|
||||
buffer.block_insert_after_signal()
|
||||
buffer.begin_user_action()
|
||||
|
||||
with buffer.freeze_notify():
|
||||
for mark in self.insert_markers:
|
||||
end_itr = buffer.get_iter_at_mark(mark)
|
||||
start_itr = end_itr.copy()
|
||||
|
||||
if not start_itr.starts_word():
|
||||
start_itr.backward_word_start()
|
||||
|
||||
if not end_itr.ends_word():
|
||||
end_itr.forward_word_end()
|
||||
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(end_itr, text, -1)
|
||||
|
||||
buffer.end_user_action()
|
||||
buffer.unblock_insert_after_signal()
|
||||
|
||||
return True
|
||||
|
||||
def move_cursor(self, source_view, step, count, extend_selection, emit):
|
||||
buffer = source_view.get_buffer()
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ class SourceBuffer(GtkSource.Buffer):
|
||||
def __init__(self):
|
||||
super(SourceBuffer, self).__init__()
|
||||
|
||||
self.is_processing_completion: bool = False
|
||||
|
||||
self._handler_ids = []
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user