Moved completers to new dir; improved completers and added word completion

This commit is contained in:
2026-02-16 01:37:13 -06:00
parent 80a4620724
commit bcd87801e6
43 changed files with 662 additions and 247 deletions

View File

@@ -4,8 +4,8 @@
import gi import gi
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GtkSource
# Application imports # Application imports
from .provider_response_cache import ProviderResponseCache from .provider_response_cache import ProviderResponseCache
@@ -20,14 +20,14 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
__gtype_name__ = 'ExampleCompletionProvider' __gtype_name__ = 'ExampleCompletionProvider'
def __init__(self): def __init__(self):
GObject.Object.__init__(self) super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache() self.response_cache: ProviderResponseCache = ProviderResponseCache()
def do_get_name(self): def do_get_name(self):
""" Returns: a new string containing the name of the provider. """ """ Returns: a new string containing the name of the provider. """
return 'Example Completion' return 'Example Code Completion'
def do_match(self, context): def do_match(self, context):
# word = context.get_word() # word = context.get_word()
@@ -43,6 +43,15 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
""" Determin position in result list along other providor results. """ """ Determin position in result list along other providor results. """
return 5 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): def do_get_activation(self):
""" The context for when a provider will show results """ """ The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE # return GtkSource.CompletionActivation.NONE
@@ -51,7 +60,17 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
return GtkSource.CompletionActivation.INTERACTIVE return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context): 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) context.add_proposals(self, proposals, True)

View File

@@ -50,10 +50,10 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_change(self, event: Code_Event_Types.TextChangedEvent): 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: In this instance, it will do 2 things:
1) always provide Hello World! (Not ideal but an option so its in the example) 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 PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
""" """
proposals = [ proposals: list[dict] = [
self.create_completion_item( {
self.matchers[ "hello" ]["label"], "label": self.matchers[ "hello" ]["label"],
self.matchers[ "hello" ]["text"], "text": self.matchers[ "hello" ]["text"],
self.matchers[ "hello" ]["info"] "info": self.matchers[ "hello" ]["info"]
) }
] ]
# Gtk Versions differ on get_iter responses... # Gtk Versions differ on get_iter responses...
@@ -90,21 +90,20 @@ class ProviderResponseCache(ProviderResponseCacheBase):
if re.match(r'.*\{\{\s*custom\.$', left_text): if re.match(r'.*\{\{\s*custom\.$', left_text):
# optionally proposed based on left search via regex # optionally proposed based on left search via regex
proposals.append( proposals.append(
self.create_completion_item( {
self.matchers[ "foo" ]["label"], "label": self.matchers[ "foo" ]["label"],
self.matchers[ "foo" ]["text"] "text": self.matchers[ "foo" ]["text"],
) "info": ""
}
) )
# optionally proposed based on left search via regex # optionally proposed based on left search via regex
proposals.append( proposals.append(
self.create_completion_item( {
self.matchers[ "bar" ]["label"], "label": self.matchers[ "bar" ]["label"],
self.matchers[ "bar" ]["text"] "text": self.matchers[ "bar" ]["text"],
) "info": ""
}
) )
return proposals return proposals

View File

@@ -4,15 +4,15 @@
import gi import gi
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GtkSource
# Application imports # Application imports
from .provider_response_cache import ProviderResponseCache 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. This code is an LSP code completion plugin for Newton.
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi # 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' __gtype_name__ = 'LSPProvider'
def __init__(self): def __init__(self):
GObject.Object.__init__(self) super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache() self.response_cache: ProviderResponseCache = ProviderResponseCache()
@@ -31,14 +31,11 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
def do_get_name(self): def do_get_name(self):
return "LSP Code Completion" 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): def do_match(self, context):
word = self.response_cache.get_word(context) word = self.response_cache.get_word(context)
if not word or len(word) < 2: return False 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() iter.backward_char()
ch = iter.get_char() ch = iter.get_char()
# NOTE: Look to re-add or apply supprting logic to use spaces # 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): def do_get_priority(self):
return 5 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): def do_get_activation(self):
""" The context for when a provider will show results """ """ The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE # return GtkSource.CompletionActivation.NONE
@@ -63,17 +67,16 @@ class Provider(GObject.Object, GtkSource.CompletionProvider):
# return GtkSource.CompletionActivation.INTERACTIVE # return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context): 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) 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

View File

@@ -30,16 +30,16 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_change(self, event: Code_Event_Types.TextChangedEvent): 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 = [ proposals = [
self.create_completion_item( {
"LSP Class", "label": "LSP Class",
"LSP Code", "text": "LSP Code",
"A test LSP completion item..." "info": "A test LSP completion item..."
) }
] ]
return proposals return proposals

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

View 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

View File

@@ -3,11 +3,9 @@ import re
# Lib imports # Lib imports
import gi import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports # Application imports
@@ -23,7 +21,7 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
__gtype_name__ = 'SnippetsCompletionProvider' __gtype_name__ = 'SnippetsCompletionProvider'
def __init__(self): def __init__(self):
GObject.Object.__init__(self) super(Provider, self).__init__()
self.response_cache: ProviderResponseCache = ProviderResponseCache() self.response_cache: ProviderResponseCache = ProviderResponseCache()
@@ -40,6 +38,13 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
def do_get_priority(self): def do_get_priority(self):
return 2 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): def do_get_activation(self):
""" The context for when a provider will show results """ """ The context for when a provider will show results """
# return GtkSource.CompletionActivation.NONE # return GtkSource.CompletionActivation.NONE

View File

@@ -52,8 +52,8 @@ class ProviderResponseCache(ProviderResponseCacheBase):
def process_file_change(self, event: Code_Event_Types.TextChangedEvent): def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
... ...
def filter(self, word: str): def filter(self, word: str) -> list[dict]:
response: list = [] response: list[dict] = []
for entry in self.matchers: for entry in self.matchers:
if not word in entry: continue if not word in entry: continue
@@ -62,5 +62,7 @@ class ProviderResponseCache(ProviderResponseCacheBase):
return response 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

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

View File

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

View File

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

View File

@@ -38,10 +38,10 @@ class ProviderResponseCacheBase:
def process_file_change(self, buffer: GtkSource.Buffer): def process_file_change(self, buffer: GtkSource.Buffer):
raise ProviderResponseCacheException("ProviderResponseCacheBase 'process_change' not implemented...") 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...") 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...") raise ProviderResponseCacheException("ProviderResponseCacheBase 'filter_with_context' not implemented...")
@@ -50,8 +50,8 @@ class ProviderResponseCacheBase:
label: str = "", label: str = "",
text: str = "", text: str = "",
info: str = "", info: str = "",
completion: any = None icon: any = None
): ) -> dict:
if not label or not text: return if not label or not text: return
comp_item = GtkSource.CompletionItem.new() comp_item = GtkSource.CompletionItem.new()
@@ -62,26 +62,58 @@ class ProviderResponseCacheBase:
comp_item.set_info(info) comp_item.set_info(info)
# comp_item.set_markup(f"<h3>{info}</h3>") # comp_item.set_markup(f"<h3>{info}</h3>")
if completion: if icon:
comp_item.set_icon( comp_item.set_icon(
self.get_icon_for_type(completion.type) self.get_icon_for_type(icon.type)
) )
return comp_item return comp_item
def get_word(self, context): def get_all_marks(self, buffer) -> list:
start_iter = context.get_iter() marks: list = []
end_iter = None iter_ = buffer.get_start_iter()
if not isinstance(start_iter, Gtk.TextIter): while iter_:
_, start_iter = context.get_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() end_iter = start_iter.copy()
if not start_iter.starts_word(): if not start_iter.starts_word():
start_iter.backward_word_start() start_iter.backward_word_start()
if not end_iter.ends_word():
end_iter.forward_word_end() end_iter.forward_word_end()
buffer = start_iter.get_buffer() buffer = start_iter.get_buffer()
return buffer.get_text(start_iter, end_iter, False) 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()

View File

@@ -17,9 +17,6 @@ class CompletionController(ControllerBase):
def __init__(self): def __init__(self):
super(CompletionController, self).__init__() 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._completers: list[GtkSource.Completion] = []
self._providers: dict[str, GtkSource.CompletionProvider] = {} self._providers: dict[str, GtkSource.CompletionProvider] = {}
@@ -45,10 +42,12 @@ class CompletionController(ControllerBase):
def register_completer(self, completer: GtkSource.Completion): def register_completer(self, completer: GtkSource.Completion):
self._completers.append(completer) self._completers.append(completer)
completer.add_provider(self.words_provider)
for provider in self._providers.values(): for provider in self._providers.values():
completer.add_provider(provider) completer.add_provider(provider)
def unregister_completer(self, completer: GtkSource.Completion):
self._completers.remove(completer)
def register_provider( def register_provider(
self, self,
provider_name: str, provider_name: str,
@@ -68,14 +67,10 @@ class CompletionController(ControllerBase):
completer.remove_provider(provider) completer.remove_provider(provider)
def provider_process_file_load(self, event: Code_Event_Types.AddedNewFileEvent): def provider_process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
self.words_provider.register(event.file.buffer)
for provider in self._providers.values(): for provider in self._providers.values():
provider.response_cache.process_file_load(event) provider.response_cache.process_file_load(event)
def provider_process_file_close(self, event: Code_Event_Types.RemovedFileEvent): def provider_process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
self.words_provider.unregister(event.file.buffer)
for provider in self._providers.values(): for provider in self._providers.values():
provider.response_cache.process_file_close(event) provider.response_cache.process_file_close(event)

View File

@@ -34,6 +34,9 @@ class SourceViewsMultiInsertState(MarkEventsMixin):
if not self.insert_markers: return False if not self.insert_markers: return False
buffer = file.buffer 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) # freeze buffer and insert to each mark (if any)
buffer.block_insert_after_signal() buffer.block_insert_after_signal()
@@ -49,6 +52,32 @@ class SourceViewsMultiInsertState(MarkEventsMixin):
return True 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): def move_cursor(self, source_view, step, count, extend_selection, emit):
buffer = source_view.get_buffer() buffer = source_view.get_buffer()

View File

@@ -13,6 +13,8 @@ class SourceBuffer(GtkSource.Buffer):
def __init__(self): def __init__(self):
super(SourceBuffer, self).__init__() super(SourceBuffer, self).__init__()
self.is_processing_completion: bool = False
self._handler_ids = [] self._handler_ids = []