diff --git a/plugins/lsp_client/lsp_controller.py b/plugins/lsp_client/lsp_controller.py index 141bb25..3a9158e 100644 --- a/plugins/lsp_client/lsp_controller.py +++ b/plugins/lsp_client/lsp_controller.py @@ -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() \ No newline at end of file + 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 \ No newline at end of file diff --git a/plugins/lsp_client/manifest.json b/plugins/lsp_client/manifest.json index 9a5072f..255c6d6 100644 --- a/plugins/lsp_client/manifest.json +++ b/plugins/lsp_client/manifest.json @@ -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:g", - "LSP Client||do_get_implementation:n" - ] + "pass_events": "true" } } } \ No newline at end of file diff --git a/plugins/lsp_client/plugin.py b/plugins/lsp_client/plugin.py index 4743c96..c249e90 100644 --- a/plugins/lsp_client/plugin.py +++ b/plugins/lsp_client/plugin.py @@ -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) - ) \ No newline at end of file diff --git a/plugins/lsp_client/pylspclient/lsp_client.py b/plugins/lsp_client/pylspclient/lsp_client.py index 1485abd..57dfd35 100644 --- a/plugins/lsp_client/pylspclient/lsp_client.py +++ b/plugins/lsp_client/pylspclient/lsp_client.py @@ -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 ) diff --git a/plugins/snippets/plugin.py b/plugins/snippets/plugin.py index c4489ba..5794b7e 100644 --- a/plugins/snippets/plugin.py +++ b/plugins/snippets/plugin.py @@ -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) \ No newline at end of file diff --git a/src/core/widgets/base/notebook/editor_events.py b/src/core/widgets/base/notebook/editor_events.py index 5c1d9ae..a3d0b44 100644 --- a/src/core/widgets/base/notebook/editor_events.py +++ b/src/core/widgets/base/notebook/editor_events.py @@ -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) diff --git a/src/core/widgets/base/sourceview/key_input_controller.py b/src/core/widgets/base/sourceview/key_input_controller.py index 866f43f..a9384a3 100644 --- a/src/core/widgets/base/sourceview/key_input_controller.py +++ b/src/core/widgets/base/sourceview/key_input_controller.py @@ -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() diff --git a/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py b/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py index 92cb12d..ec942d0 100644 --- a/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py +++ b/src/core/widgets/base/sourceview/mixins/source_file_events_mixin.py @@ -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: - - - diff --git a/src/core/widgets/base/sourceview/source_view_controller.py b/src/core/widgets/base/sourceview/source_view_controller.py index 6536207..447a9b9 100644 --- a/src/core/widgets/base/sourceview/source_view_controller.py +++ b/src/core/widgets/base/sourceview/source_view_controller.py @@ -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 diff --git a/user_config/usr/share/newton/lsp_servers_config.json b/user_config/usr/share/newton/lsp_servers_config.json index 3f90d1f..7badb9a 100644 --- a/user_config/usr/share/newton/lsp_servers_config.json +++ b/user_config/usr/share/newton/lsp_servers_config.json @@ -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/",