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: class LSPController:
def __init__(self): def __init__(self, lsp_servers_config = {}):
super().__init__() super().__init__()
self.lsp_servers_config = lsp_servers_config
self.lsp_clients = {} 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): def create_client(self, language = "", server_proc = None, initialization_options = None):
if not language or not server_proc: return False if not language or not server_proc: return False
@ -51,23 +80,6 @@ class LSPController:
return True 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: [] = []): def create_lsp_server(self, server_command: [] = []):
if not server_command: return None if not server_command: return None
@ -78,17 +90,55 @@ class LSPController:
return server_proc return server_proc
def blame(self, response): def do_open(self, language_id, uri):
for d in response['diagnostics']: if language_id in self.lsp_clients.keys():
if d['severity'] == 1: lsp_client = self.lsp_clients[language_id]
print(f"An error occurs in {response['uri']} at {d['range']}:") else:
print(f"\t[{d['source']}] {d['message']}") lsp_client = self.load_lsp_server(language_id)
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
def _shutting_down(self): text = open(uri[7:], "r").read()
keys = self.lsp_clients.keys() version = 1
for key in keys:
print(f"LSP Server: ( {key} ) Shutting Down...") lsp_client.didOpen(
self.lsp_clients[key].shutdown() pylspclient.lsp_structs.TextDocumentItem(uri, language_id, version, text)
self.lsp_clients[key].exit() )
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", "credit": "Avi Yeger for the pylspclient used by this plugin. Link: https://github.com/yeger00/pylspclient",
"support": "", "support": "",
"requests": { "requests": {
"pass_events": "true", "pass_events": "true"
"bind_keys": [
"LSP Client||do_goto:<Control>g",
"LSP Client||do_get_implementation:<Control>n"
]
} }
} }
} }

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_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
self.lsp_client = None
self.lsp_disabled = False
def generate_reference_ui_element(self): def generate_reference_ui_element(self):
... ...
@ -38,97 +36,41 @@ class Plugin(PluginBase):
with open(self.lsp_config_path, "r") as f: with open(self.lsp_config_path, "r") as f:
self.lsp_servers_config = json.load(f) self.lsp_servers_config = json.load(f)
else: else:
self.lsp_disabled = True
text = f"LSP NOT Enabled.\nFile:\n\t{self.lsp_config_path}\ndoes no exsist..." 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,)) self._event_system.emit("bubble_message", ("warning", self.name, text,))
return
if not self.lsp_disabled: self.lsp_controller = LSPController(self.lsp_servers_config)
self.lsp_controller = LSPController() self.inner_subscribe_to_events()
# 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))
def subscribe_to_events(self): def subscribe_to_events(self):
...
def inner_subscribe_to_events(self):
self._event_system.subscribe("shutting_down", self._shutting_down) 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("buffer_changed", self._buffer_changed)
self._event_system.subscribe("do_goto", self._do_goto) self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open)
self._event_system.subscribe("do_get_implementation", self._do_get_implementation) 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): def _shutting_down(self):
if self.lsp_controller: if self.lsp_controller:
self.lsp_controller._shutting_down() 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): def _buffer_changed(self, buffer):
if self.lsp_disabled: return # self._do_completion()
self._do_completion() ...
def _do_completion(self, is_invoked = False): 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() ) iter = self._buffer.get_iter_at_mark( self._buffer.get_insert() )
line = iter.get_line() line = iter.get_line()
offset = iter.get_line_offset() offset = iter.get_line_offset()
@ -143,7 +85,7 @@ class Plugin(PluginBase):
else: else:
trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerForIncompleteCompletions trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerForIncompleteCompletions
result = self.lsp_client.completion( result = self.lsp_controller.completion(
pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.TextDocumentIdentifier(uri),
pylspclient.lsp_structs.Position(line, offset), pylspclient.lsp_structs.Position(line, offset),
pylspclient.lsp_structs.CompletionContext(trigger, _char) pylspclient.lsp_structs.CompletionContext(trigger, _char)
@ -155,19 +97,9 @@ class Plugin(PluginBase):
else: else:
print(result.label) print(result.label)
def _do_goto(self): def _do_goto(self, language_id, uri, line, offset):
if self.lsp_disabled: return 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: if len(results) == 1:
result = results[0] result = results[0]
file = result.uri[7:] file = result.uri[7:]
@ -175,20 +107,3 @@ class Plugin(PluginBase):
message = f"FILE|{file}:{line}" message = f"FILE|{file}:{line}"
self._event_system.emit("post_file_to_ipc", message) 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. :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) 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): def didClose(self, textDocument):
"""
:param TextDocumentIdentifier textDocument: The document that was closed.
"""
return self.lsp_endpoint.send_notification("textDocument/didClose", textDocument = textDocument) return self.lsp_endpoint.send_notification("textDocument/didClose", textDocument = textDocument)
def didChange(self, textDocument, contentChanges): def didChange(self, textDocument, contentChanges):
@ -182,7 +191,7 @@ class LspClient(object):
:param Position position: The position inside the text document. :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, textDocument = textDocument,
position = position 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 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 # where self.name should not be needed for message comms
self.snippet_data = None self.snippet_data = None
self.file_type = None self._file_type = None
self.active_snippit_group = None self.active_snippit_group = None
self.snippit_groups = [] self.snippit_groups = []
self.snippit_prefixes = [] self.snippit_prefixes = []
@ -42,16 +42,16 @@ class Plugin(PluginBase):
def _set_active_src_view(self, source_view): def _set_active_src_view(self, source_view):
self._active_src_view = 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._tag_table = self._buffer.get_tag_table()
self.file_type = source_view.get_filetype()
self.load_target_snippt_group() self.load_target_snippt_group()
def load_target_snippt_group(self): def load_target_snippt_group(self):
self.active_snippit_group = None self.active_snippit_group = None
for group in self.snippit_groups: for group in self.snippit_groups:
if group in self.file_type: if group in self._file_type:
self.active_snippit_group = group self.active_snippit_group = group
break break

View File

@ -43,6 +43,11 @@ class EditorEventsMixin:
if notebook.NAME == "notebook_1" and notebook.get_n_pages() == 1: if notebook.NAME == "notebook_1" and notebook.get_n_pages() == 1:
return 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) page_num = notebook.page_num(container)
source_view._cancel_current_file_watchers() source_view._cancel_current_file_watchers()
notebook.remove_page(page_num) notebook.remove_page(page_num)

View File

@ -81,7 +81,7 @@ class KeyInputController:
return True 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": if keyname == "z":
self.keyboard_undo() self.keyboard_undo()
if keyname == "y": if keyname == "y":
@ -92,6 +92,8 @@ class KeyInputController:
self.save_file() self.save_file()
if keyname == "h": if keyname == "h":
self.toggle_highlight_line() self.toggle_highlight_line()
if keyname == "g":
self.go_to_call()
if keyname == "equal": if keyname == "equal":
self.scale_up_text() self.scale_up_text()

View File

@ -137,8 +137,11 @@ class FileEventsMixin:
self._completion.remove_provider(provider) self._completion.remove_provider(provider)
file = self._current_file.get_path() file = self._current_file.get_path()
uri = self._current_file.get_uri()
buffer = self.get_buffer() buffer = self.get_buffer()
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
word_completion = GtkSource.CompletionWords.new("word_completion") word_completion = GtkSource.CompletionWords.new("word_completion")
word_completion.register(buffer) word_completion.register(buffer)
self._completion.add_provider(word_completion) 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): def set_buffer_style(self, buffer, style = settings.theming.syntax_theme):
buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) ) 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): def update_cursor_position(self, buffer = None):
buffer = self.get_buffer() if not buffer else buffer buffer = self.get_buffer() if not buffer else buffer

View File

@ -5,14 +5,34 @@
"initialization_options": {} "initialization_options": {}
}, },
"python": { "python": {
"info": "https://pypi.org/project/jedi-language-server/", "info": "https://github.com/python-lsp/python-lsp-server",
"command": ["jedi-language-server"], "command": ["pylsp"],
"initialization_options": {} "initialization_options": {}
}, },
"python3": { "python3": {
"info": "https://pypi.org/project/jedi-language-server/", "info": "https://pypi.org/project/jedi-language-server/",
"command": ["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": { "c": {
"info": "https://clangd.llvm.org/", "info": "https://clangd.llvm.org/",