generated from itdominator/Python-With-Gtk-Template
Reworking LSP logic and plugin
This commit is contained in:
parent
839f4df5ef
commit
cb07afb1b0
@ -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
|
||||
@ -51,23 +80,6 @@ class LSPController:
|
||||
|
||||
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
|
||||
|
||||
@ -78,17 +90,55 @@ class LSPController:
|
||||
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)
|
||||
|
||||
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):
|
||||
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()
|
||||
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
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
)
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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/",
|
||||
|
Loading…
Reference in New Issue
Block a user