Reworking completion call and didChange logic

This commit is contained in:
itdominator 2024-01-29 22:11:14 -06:00
parent 0c6af185a2
commit defff39e25
7 changed files with 79 additions and 65 deletions

View File

@ -13,7 +13,10 @@ from .capabilities import Capabilities
class ReadPipe(threading.Thread): class ReadPipe(threading.Thread):
def __init__(self, pipe): def __init__(self, pipe):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.pipe = pipe
self.daemon = True
self.pipe = pipe
def run(self): def run(self):
line = self.pipe.readline().decode('utf-8') line = self.pipe.readline().decode('utf-8')
@ -63,7 +66,7 @@ class LSPController:
if not language or not server_proc: return False if not language or not server_proc: return False
root_path = None root_path = None
# root_uri = 'file:///home/abaddon/Coding/Projects/Active/C_n_CPP_Projects/gtk/Newton/src/' # root_uri = 'file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/Newton_Editor/src/'
# workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}] # workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}]
root_uri = '' root_uri = ''
workspace_folders = [{'name': '', 'uri': root_uri}] workspace_folders = [{'name': '', 'uri': root_uri}]
@ -76,6 +79,7 @@ class LSPController:
initializationOptions = initialization_options, \ initializationOptions = initialization_options, \
capabilities = Capabilities.data, \ capabilities = Capabilities.data, \
trace = "off", \ trace = "off", \
# trace = "on", \
workspaceFolders = workspace_folders workspaceFolders = workspace_folders
) )
@ -123,16 +127,17 @@ class LSPController:
return [] return []
def do_change(self, language_id, line, start, end, text): def do_change(self, uri, language_id, line, start, end, text):
if language_id in self.lsp_clients.keys(): if language_id in self.lsp_clients.keys():
start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset()) start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset())
end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset()) end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset())
range_info = pylspclient.lsp_structs.Range(start_pos, end_pos) range_info = pylspclient.lsp_structs.Range(start_pos, end_pos)
text_length = len(text) text_length = len(text)
change_event = pylspclient.lsp_structs.TextDocumentContentChangeEvent(range_info, text_length, text) text_document = pylspclient.lsp_structs.TextDocumentItem(uri, language_id, 1, text)
change_event = pylspclient.lsp_structs.TextDocumentContentChangeEvent(range_info, text_length, text)
return self.lsp_clients[language_id].didChange( None, change_event ) return self.lsp_clients[language_id].didChange( text_document, change_event )
return [] return []
@ -155,7 +160,7 @@ class LSPController:
) )
return [] return []
def load_lsp_server(self, language_id): def load_lsp_server(self, language_id):
if not language_id in self.lsp_servers_config.keys(): if not language_id in self.lsp_servers_config.keys():

View File

@ -24,7 +24,7 @@ class Plugin(PluginBase):
super().__init__() super().__init__()
self.name = "LSP Client" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus self.name = "LSP Client" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms # where self.name should not be needed for message comms
self.lsp_config_path: str = os.path.dirname(os.path.realpath(__file__)) + "/../../lsp_servers_config.json" self.lsp_config_path: str = os.path.dirname(os.path.realpath(__file__)) + "/../../lsp_servers_config.json"
self.lsp_servers_config: dict = {} self.lsp_servers_config: dict = {}
self.lsp_controller = None self.lsp_controller = None
@ -58,24 +58,11 @@ class Plugin(PluginBase):
self._event_system.subscribe("textDocument/didSave", self.lsp_controller.do_save) self._event_system.subscribe("textDocument/didSave", self.lsp_controller.do_save)
self._event_system.subscribe("textDocument/didClose", self.lsp_controller.do_close) self._event_system.subscribe("textDocument/didClose", self.lsp_controller.do_close)
self._event_system.subscribe("textDocument/definition", self._do_goto) self._event_system.subscribe("textDocument/definition", self._do_goto)
self._event_system.subscribe("textDocument/completion", self.completion) self._event_system.subscribe("textDocument/completion", self._do_completion)
def _shutting_down(self): def _shutting_down(self):
self.lsp_controller._shutting_down() self.lsp_controller._shutting_down()
def cancel_timer(self):
if self.timer:
self.timer.cancel()
GLib.idle_remove_by_data(None)
def delay_completion_glib(self, source_view, context, callback):
GLib.idle_add(self._do_completion, source_view, context, callback)
def delay_completion(self, source_view, context, callback):
self.timer = threading.Timer(0.8, self.delay_completion_glib, (source_view, context, callback,))
self.timer.daemon = True
self.timer.start()
def _buffer_changed(self, language_id, buffer): def _buffer_changed(self, language_id, buffer):
iter = buffer.get_iter_at_mark( buffer.get_insert() ) iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() line = iter.get_line()
@ -86,14 +73,10 @@ class Plugin(PluginBase):
start.forward_line() start.forward_line()
end.forward_to_line_end() end.forward_to_line_end()
text = buffer.get_text(start, end, include_hidden_chars = False) text = buffer.get_text(start, end, include_hidden_chars = False)
result = self.lsp_controller.do_change(language_id, line, start, end, text) result = self.lsp_controller.do_change(buffer.uri, language_id, line, start, end, text)
def completion(self, source_view, context, callback): def _do_completion(self, source_view):
self.cancel_timer()
self.delay_completion(source_view, context, callback)
def _do_completion(self, source_view, context, callback):
filepath = source_view.get_current_file() filepath = source_view.get_current_file()
if not filepath: return if not filepath: return
@ -101,16 +84,22 @@ class Plugin(PluginBase):
uri = filepath.get_uri() uri = filepath.get_uri()
buffer = source_view.get_buffer() buffer = source_view.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() ) iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() + 1 line = iter.get_line()
_char = iter.get_char() _char = iter.get_char()
# if iter.backward_char(): if iter.backward_char():
# _char = iter.get_char() _char = iter.get_char()
offset = iter.get_line_index() + 1 offset = iter.get_line_offset()
# offset = iter.get_line_offset() result = self.lsp_controller.do_completion(
result = self.lsp_controller.do_completion(source_view.get_filetype(), uri, line, offset, _char) source_view.get_filetype(),
callback(context, result) uri,
line,
offset,
_char
)
return result
def _do_goto(self, language_id, uri, line, offset): def _do_goto(self, language_id, uri, line, offset):
results = self.lsp_controller.do_goto(language_id, uri, line, offset) results = self.lsp_controller.do_goto(language_id, uri, line, offset)

View File

@ -1,3 +1,8 @@
# Python imports
# Lib imports
# Application imports
from . import lsp_structs from . import lsp_structs
@ -40,13 +45,13 @@ class LspClient(object):
""" """
self.lsp_endpoint.start() self.lsp_endpoint.start()
return self.lsp_endpoint.call_method("initialize", \ return self.lsp_endpoint.call_method("initialize",
processId = processId, \ processId = processId,
rootPath = rootPath, \ rootPath = rootPath,
rootUri = rootUri, \ rootUri = rootUri,
initializationOptions = initializationOptions, \ initializationOptions = initializationOptions,
capabilities = capabilities, \ capabilities = capabilities,
trace = trace, \ trace = trace,
workspaceFolders = workspaceFolders workspaceFolders = workspaceFolders
) )
@ -61,18 +66,12 @@ class LspClient(object):
def shutdown(self): def shutdown(self):
""" """
The initialized notification is sent from the client to the server after the client received the result of the initialize request
but before the client is sending any other request or notification to the server. The server can use the initialized notification
for example to dynamically register capabilities. The initialized notification may only be sent once.
""" """
return self.lsp_endpoint.call_method("shutdown") return self.lsp_endpoint.call_method("shutdown")
def exit(self): def exit(self):
""" """
The initialized notification is sent from the client to the server after the client received the result of the initialize request
but before the client is sending any other request or notification to the server. The server can use the initialized notification
for example to dynamically register capabilities. The initialized notification may only be sent once.
""" """
self.lsp_endpoint.send_notification("exit") self.lsp_endpoint.send_notification("exit")
@ -115,7 +114,7 @@ class LspClient(object):
The document change notification is sent from the client to the server to signal changes to a text document. The document change notification is sent from the client to the server to signal changes to a text document.
In 2.0 the shape of the params has changed to include proper version numbers and language ids. In 2.0 the shape of the params has changed to include proper version numbers and language ids.
:param VersionedTextDocumentIdentifier textDocument: The initial trace setting. If omitted trace is disabled ('off'). :param TextDocumentItem textDocument: The text document.
:param TextDocumentContentChangeEvent[] contentChanges: The actual content changes. The content changes describe single state changes :param TextDocumentContentChangeEvent[] contentChanges: The actual content changes. The content changes describe single state changes
to the document. So if there are two content changes c1 and c2 for a document in state S then c1 move the document to the document. So if there are two content changes c1 and c2 for a document in state S then c1 move the document
to S' and c2 to S''. to S' and c2 to S''.

View File

@ -39,7 +39,13 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']: if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False return False
event_system.emit("textDocument/completion", (self._source_view, context, self.do_populate)) ch = iter.get_char()
# NOTE: Look to re-add or apply supprting logic to use spaces
# As is it slows down the editor in certain contexts...
if not (ch in ('_', '.', ' ') or ch.isalnum()):
# if not (ch in ('_', '.') or ch.isalnum()):
return False
return True return True
def do_get_priority(self): def do_get_priority(self):
@ -49,7 +55,9 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
return GtkSource.CompletionActivation.INTERACTIVE return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context, result = None): def do_populate(self, context, result = None):
result = event_system.emit_and_await("textDocument/completion", (self._source_view,))
proposals = [] proposals = []
if result: if result:
if not result.items is None: if not result.items is None:
for item in result.items: for item in result.items:
@ -86,6 +94,5 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
comp_item.set_icon( self.get_icon_for_type(item.kind) ) comp_item.set_icon( self.get_icon_for_type(item.kind) )
comp_item.set_info(item.documentation) comp_item.set_info(item.documentation)
return comp_item
return comp_item

View File

@ -6,6 +6,7 @@ import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gio from gi.repository import Gio
from gi.repository import GtkSource from gi.repository import GtkSource
@ -79,7 +80,7 @@ class FileEventsMixin:
self.update_labels(gfile) self.update_labels(gfile)
self._loading_file = False self._loading_file = False
self._file_loader.load_async(io_priority = 80, self._file_loader.load_async(io_priority = GLib.PRIORITY_HIGH,
cancellable = None, cancellable = None,
progress_callback = None, progress_callback = None,
progress_callback_data = None, progress_callback_data = None,
@ -140,8 +141,9 @@ class FileEventsMixin:
for provider in self._completion.get_providers(): for provider in self._completion.get_providers():
self._completion.remove_provider(provider) self._completion.remove_provider(provider)
uri = self._current_file.get_uri() uri = self._current_file.get_uri()
buffer = self.get_buffer() buffer = self.get_buffer()
buffer.uri = uri
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,)) event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
@ -152,6 +154,4 @@ class FileEventsMixin:
lsp_completion_provider = LSPCompletionProvider(self) lsp_completion_provider = LSPCompletionProvider(self)
self._completion.add_provider(lsp_completion_provider) self._completion.add_provider(lsp_completion_provider)
self.got_to_line(buffer, line) self.got_to_line(buffer, line)

View File

@ -70,4 +70,4 @@ class EventSystem(Singleton):
if not response in (None, ''): if not response in (None, ''):
break break
return response return response

View File

@ -12,13 +12,27 @@ function main() {
files=() files=()
for f in "$@"; do for f in "$@"; do
# ff=${f%:*}
# ii=${f#*:}
# target=$(readlink -f "${ff}")
target=$(readlink -f "${f}") target=$(readlink -f "${f}")
i="${#files[@]}" i="${#files[@]}"
size=$(($i + 1)) size=$(($i + 1))
files[$size]="${target}" files[$size]="${target}"
# if [ -n "$ii" ]; then
# files[$size]="${target}:${ii}"
# else
# files[$size]="${target}${ii}"
# fi
done done
echo "${files[@]}"
cd "/opt/" cd "/opt/"
python /opt/newton.zip "${files[@]}" python /opt/newton.zip "${files[@]}"
} }
main "$@"; main "$@";