Reworking LSP logic and plugin

This commit is contained in:
itdominator 2023-11-06 23:01:18 -06:00
parent 839f4df5ef
commit cb07afb1b0
10 changed files with 160 additions and 154 deletions

View File

@ -23,11 +23,40 @@ class ReadPipe(threading.Thread):
class LSPController:
def __init__(self):
def __init__(self, lsp_servers_config = {}):
super().__init__()
self.lsp_servers_config = lsp_servers_config
self.lsp_clients = {}
def _blame(self, response):
for d in response['diagnostics']:
if d['severity'] == 1:
print(f"An error occurs in {response['uri']} at {d['range']}:")
print(f"\t[{d['source']}] {d['message']}")
def _shutting_down(self):
keys = self.lsp_clients.keys()
for key in keys:
print(f"LSP Server: ( {key} ) Shutting Down...")
self.lsp_clients[key].shutdown()
self.lsp_clients[key].exit()
def _generate_client(self, language = "", server_proc = None):
if not language or not server_proc: return False
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(server_proc.stdin, server_proc.stdout)
callbacks = {
"textDocument/symbolStatus": print,
"textDocument/publishDiagnostics": self._blame,
}
lsp_endpoint = pylspclient.LspEndpoint(json_rpc_endpoint, notify_callbacks = callbacks)
lsp_client = pylspclient.LspClient(lsp_endpoint)
self.lsp_clients[language] = lsp_client
return lsp_client
def create_client(self, language = "", server_proc = None, initialization_options = None):
if not language or not server_proc: return False
@ -50,23 +79,6 @@ class LSPController:
lsp_client.initialized()
return True
def _generate_client(self, language = "", server_proc = None):
if not language or not server_proc: return False
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(server_proc.stdin, server_proc.stdout)
callbacks = {
"textDocument/symbolStatus": print,
"textDocument/publishDiagnostics": self.blame,
}
lsp_endpoint = pylspclient.LspEndpoint(json_rpc_endpoint, notify_callbacks = callbacks)
lsp_client = pylspclient.LspClient(lsp_endpoint)
self.lsp_clients[language] = lsp_client
return lsp_client
def create_lsp_server(self, server_command: [] = []):
if not server_command: return None
@ -76,19 +88,57 @@ class LSPController:
read_pipe.start()
return server_proc
def blame(self, response):
for d in response['diagnostics']:
if d['severity'] == 1:
print(f"An error occurs in {response['uri']} at {d['range']}:")
print(f"\t[{d['source']}] {d['message']}")
def do_open(self, language_id, uri):
if language_id in self.lsp_clients.keys():
lsp_client = self.lsp_clients[language_id]
else:
lsp_client = self.load_lsp_server(language_id)
def _shutting_down(self):
keys = self.lsp_clients.keys()
for key in keys:
print(f"LSP Server: ( {key} ) Shutting Down...")
self.lsp_clients[key].shutdown()
self.lsp_clients[key].exit()
if lsp_client:
self.register_opened_file(language_id, uri, lsp_client)
def do_save(self, language_id, uri):
if language_id in self.lsp_clients.keys():
self.lsp_clients[language_id].didSave(
pylspclient.lsp_structs.TextDocumentIdentifier(uri)
)
def do_close(self, language_id, uri):
if language_id in self.lsp_clients.keys():
self.lsp_clients[language_id].didClose(
pylspclient.lsp_structs.TextDocumentIdentifier(uri)
)
def do_goto(self, language_id, uri, line, offset):
if language_id in self.lsp_clients.keys():
return self.lsp_clients[language_id].definition(
pylspclient.lsp_structs.TextDocumentIdentifier(uri),
pylspclient.lsp_structs.Position(line, offset)
)
def register_opened_file(self, language_id = "", uri = "", lsp_client = None):
if not language_id or not uri: return
text = open(uri[7:], "r").read()
version = 1
lsp_client.didOpen(
pylspclient.lsp_structs.TextDocumentItem(uri, language_id, version, text)
)
def load_lsp_server(self, language_id):
command = self.lsp_servers_config[language_id]["command"]
config_options = self.lsp_servers_config[language_id]["initialization_options"]
if command:
server_proc = self.create_lsp_server(command)
client_created = self.create_client(language_id, server_proc, config_options)
if client_created:
return self.lsp_clients[language_id]
text = f"LSP could not be created for file type: {language_id} ..."
self._event_system.emit("bubble_message", ("warning", self.name, text,))
return None

View File

@ -6,11 +6,7 @@
"credit": "Avi Yeger for the pylspclient used by this plugin. Link: https://github.com/yeger00/pylspclient",
"support": "",
"requests": {
"pass_events": "true",
"bind_keys": [
"LSP Client||do_goto:<Control>g",
"LSP Client||do_get_implementation:<Control>n"
]
"pass_events": "true"
}
}
}

View File

@ -27,8 +27,6 @@ class Plugin(PluginBase):
self.lsp_config_path: str = os.path.dirname(os.path.realpath(__file__)) + "/../../lsp_servers_config.json"
self.lsp_servers_config: dict = {}
self.lsp_controller = None
self.lsp_client = None
self.lsp_disabled = False
def generate_reference_ui_element(self):
...
@ -38,97 +36,41 @@ class Plugin(PluginBase):
with open(self.lsp_config_path, "r") as f:
self.lsp_servers_config = json.load(f)
else:
self.lsp_disabled = True
text = f"LSP NOT Enabled.\nFile:\n\t{self.lsp_config_path}\ndoes no exsist..."
self._event_system.emit("bubble_message", ("warning", self.name, text,))
return
if not self.lsp_disabled:
self.lsp_controller = LSPController()
# language_id = pylspclient.lsp_structs.LANGUAGE_IDENTIFIER.C
# version = 1
# self.lsp_client.didOpen(pylspclient.lsp_structs.TextDocumentItem(uri, language_id, version, text))
# try:
# symbols = self.lsp_client.documentSymbol(pylspclient.lsp_structs.TextDocumentIdentifier(uri))
# for symbol in symbols:
# print(symbol.name)
# except pylspclient.lsp_structs.ResponseError:
# documentSymbol is supported from version 8.
# print("Failed to document symbols")
# ...
# self.lsp_client.definition(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4))
# self.lsp_client.signatureHelp(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4))
# self.lsp_client.definition(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4))
# self.lsp_client.completion(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4), pylspclient.lsp_structs.CompletionContext(pylspclient.lsp_structs.CompletionTriggerKind.Invoked))
self.lsp_controller = LSPController(self.lsp_servers_config)
self.inner_subscribe_to_events()
def subscribe_to_events(self):
...
def inner_subscribe_to_events(self):
self._event_system.subscribe("shutting_down", self._shutting_down)
self._event_system.subscribe("set_active_src_view", self._set_active_src_view)
self._event_system.subscribe("buffer_changed_first_load", self._buffer_changed_first_load)
self._event_system.subscribe("buffer_changed", self._buffer_changed)
self._event_system.subscribe("do_goto", self._do_goto)
self._event_system.subscribe("do_get_implementation", self._do_get_implementation)
self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open)
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/definition", self._do_goto)
def _shutting_down(self):
if self.lsp_controller:
self.lsp_controller._shutting_down()
def _set_active_src_view(self, source_view):
if self.lsp_disabled: return
self._active_src_view = source_view
self._buffer = source_view.get_buffer()
self._file_type = source_view.get_filetype()
if self._file_type in self.lsp_servers_config.keys():
self.set_lsp_server()
else:
text = f"LSP could not be created for file type: {self._file_type} ..."
self._event_system.emit("bubble_message", ("warning", self.name, text,))
def set_lsp_server(self):
if self._file_type in self.lsp_controller.lsp_clients.keys():
self.lsp_client = self.lsp_controller.lsp_clients[self._file_type]
else:
self.lsp_client = self.load_lsp_server()
if self.lsp_client:
# Note: textDocument/didClose is actually called from the open api beforehand
# to insure no more than one instanmce of a file is opened
uri = self._active_src_view.get_current_filepath().get_uri()
self.register_opened_file(self._file_type, uri)
def load_lsp_server(self):
command = self.lsp_servers_config[self._file_type]["command"]
if command:
server_proc = self.lsp_controller.create_lsp_server(command)
client_created = self.lsp_controller.create_client(self._file_type, server_proc)
if client_created:
return self.lsp_controller.lsp_clients[self._file_type]
text = f"LSP could not be created for file type: {self._file_type} ..."
self._event_system.emit("bubble_message", ("warning", self.name, text,))
return None
def _buffer_changed_first_load(self, buffer):
if self.lsp_disabled: return
self._buffer = buffer
def _buffer_changed(self, buffer):
if self.lsp_disabled: return
self._do_completion()
# self._do_completion()
...
def _do_completion(self, is_invoked = False):
if self.lsp_disabled: return
fpath = self._active_src_view.get_current_filepath()
uri = self._active_src_view.get_current_filepath().get_uri()
if not fpath: return
uri = fpath.get_uri()
iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() )
line = iter.get_line()
offset = iter.get_line_offset()
@ -143,7 +85,7 @@ class Plugin(PluginBase):
else:
trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerForIncompleteCompletions
result = self.lsp_client.completion(
result = self.lsp_controller.completion(
pylspclient.lsp_structs.TextDocumentIdentifier(uri),
pylspclient.lsp_structs.Position(line, offset),
pylspclient.lsp_structs.CompletionContext(trigger, _char)
@ -155,19 +97,9 @@ class Plugin(PluginBase):
else:
print(result.label)
def _do_goto(self):
if self.lsp_disabled: return
def _do_goto(self, language_id, uri, line, offset):
results = self.lsp_controller.do_goto(language_id, uri, line, offset)
iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() )
line = iter.get_line()
offset = iter.get_line_offset()
uri = self._active_src_view.get_current_filepath().get_uri()
results = self.lsp_client.definition(
pylspclient.lsp_structs.TextDocumentIdentifier(uri),
pylspclient.lsp_structs.Position(line, offset)
)
results = []
if len(results) == 1:
result = results[0]
file = result.uri[7:]
@ -175,20 +107,3 @@ class Plugin(PluginBase):
message = f"FILE|{file}:{line}"
self._event_system.emit("post_file_to_ipc", message)
def _do_get_implementation(self):
if self.lsp_disabled: return
results = self.lsp_client.declaration(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(line, offset))
def register_opened_file(self, language_id = "", uri = ""):
if not language_id or not uri: return
text = open(uri[7:], "r").read()
version = 1
self.lsp_client.didClose(
pylspclient.lsp_structs.TextDocumentItem(uri, language_id, version, text)
)
self.lsp_client.didOpen(
pylspclient.lsp_structs.TextDocumentItem(uri, language_id, version, text)
)

View File

@ -95,10 +95,19 @@ class LspClient(object):
:param TextDocumentItem textDocument: The document that was opened.
"""
self.lsp_endpoint.send_notification("textDocument/didClose", textDocument = textDocument)
return self.lsp_endpoint.send_notification("textDocument/didOpen", textDocument = textDocument)
def didSave(self, textDocument):
"""
:param TextDocumentIdentifier textDocument: The document that was saved.
"""
return self.lsp_endpoint.send_notification("textDocument/didSave", textDocument = textDocument)
def didClose(self, textDocument):
"""
:param TextDocumentIdentifier textDocument: The document that was closed.
"""
return self.lsp_endpoint.send_notification("textDocument/didClose", textDocument = textDocument)
def didChange(self, textDocument, contentChanges):
@ -182,7 +191,7 @@ class LspClient(object):
:param Position position: The position inside the text document.
"""
result_dict = self.lsp_endpoint.call_method("textDocument/definition",
result_dict = self.lsp_endpoint.call_method("textDocument/typeDefinition",
textDocument = textDocument,
position = position
)

View File

@ -18,7 +18,7 @@ class Plugin(PluginBase):
self.name = "Snippets" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
self.snippet_data = None
self.file_type = None
self._file_type = None
self.active_snippit_group = None
self.snippit_groups = []
self.snippit_prefixes = []
@ -42,16 +42,16 @@ class Plugin(PluginBase):
def _set_active_src_view(self, source_view):
self._active_src_view = source_view
self._buffer = self._active_src_view.get_buffer()
self._buffer = source_view.get_buffer()
self._file_type = source_view.get_filetype()
self._tag_table = self._buffer.get_tag_table()
self.file_type = source_view.get_filetype()
self.load_target_snippt_group()
def load_target_snippt_group(self):
self.active_snippit_group = None
for group in self.snippit_groups:
if group in self.file_type:
if group in self._file_type:
self.active_snippit_group = group
break
@ -99,4 +99,4 @@ class Plugin(PluginBase):
body = self.snippet_data[self.active_snippit_group][key]["body"]
snippits.append(body)
print(snippits)
print(snippits)

View File

@ -43,6 +43,11 @@ class EditorEventsMixin:
if notebook.NAME == "notebook_1" and notebook.get_n_pages() == 1:
return
file_type = source_view.get_filetype()
if not file_type == "buffer":
uri = source_view.get_current_filepath().get_uri()
event_system.emit("textDocument/didClose", (file_type, uri,))
page_num = notebook.page_num(container)
source_view._cancel_current_file_watchers()
notebook.remove_page(page_num)

View File

@ -81,7 +81,7 @@ class KeyInputController:
return True
if keyname in ["z", "y", "m", "s", "h", "equal", "minus", "Up", "Down"]:
if keyname in ["z", "y", "m", "s", "h", "g", "equal", "minus", "Up", "Down"]:
if keyname == "z":
self.keyboard_undo()
if keyname == "y":
@ -92,6 +92,8 @@ class KeyInputController:
self.save_file()
if keyname == "h":
self.toggle_highlight_line()
if keyname == "g":
self.go_to_call()
if keyname == "equal":
self.scale_up_text()

View File

@ -137,8 +137,11 @@ class FileEventsMixin:
self._completion.remove_provider(provider)
file = self._current_file.get_path()
uri = self._current_file.get_uri()
buffer = self.get_buffer()
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
word_completion = GtkSource.CompletionWords.new("word_completion")
word_completion.register(buffer)
self._completion.add_provider(word_completion)
@ -158,6 +161,3 @@ class FileEventsMixin:

View File

@ -25,6 +25,15 @@ class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
def set_buffer_style(self, buffer, style = settings.theming.syntax_theme):
buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) )
def go_to_call(self):
buffer = self.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line()
offset = iter.get_line_offset()
uri = self.get_current_filepath().get_uri()
event_system.emit("textDocument/definition", (self.get_filetype(), uri, line, offset,))
def update_cursor_position(self, buffer = None):
buffer = self.get_buffer() if not buffer else buffer

View File

@ -5,14 +5,34 @@
"initialization_options": {}
},
"python": {
"info": "https://pypi.org/project/jedi-language-server/",
"command": ["jedi-language-server"],
"info": "https://github.com/python-lsp/python-lsp-server",
"command": ["pylsp"],
"initialization_options": {}
},
"python3": {
"info": "https://pypi.org/project/jedi-language-server/",
"command": ["jedi-language-server"],
"initialization_options": {}
"initialization_options": {
"jediSettings": {
"autoImportModules": [],
"caseInsensitiveCompletion": true,
"debug": false
},
"completion": {
"disableSnippets": false,
"resolveEagerly": false,
"ignorePatterns": []
},
"markupKindPreferred": "markdown",
"workspace": {
"extraPaths": [],
"environmentPath": "/path/to/venv/bin/python",
"symbols": {
"ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"],
"maxSymbols": 20
}
}
}
},
"c": {
"info": "https://clangd.llvm.org/",