From 4d6519784527086727e7a96db7eacf426182a868 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Mon, 16 Sep 2024 23:30:22 -0500 Subject: [PATCH] Moved lsp manager to lsp client --- .../gtksourceview/lsp_client/capabilities.py | 201 ------- .../{lsp_manager => lsp_client}/client_ipc.py | 0 .../{lsp_manager => lsp_client}/config.json | 1 + .../lsp_client/lsp_controller.py | 187 ------ .../lsp_message_structs.py | 0 .../gtksourceview/lsp_client/manifest.json | 12 +- plugins/gtksourceview/lsp_client/plugin.py | 260 ++++++-- .../lsp_client/pylspclient/LICENSE | 21 - .../lsp_client/pylspclient/__init__.py | 6 - .../pylspclient/json_rpc_endpoint.py | 105 ---- .../lsp_client/pylspclient/lsp_client.py | 257 -------- .../lsp_client/pylspclient/lsp_endpoint.py | 111 ---- .../lsp_client/pylspclient/lsp_structs.py | 566 ------------------ plugins/gtksourceview/lsp_manager/__init__.py | 3 - plugins/gtksourceview/lsp_manager/__main__.py | 3 - .../gtksourceview/lsp_manager/manifest.json | 14 - plugins/gtksourceview/lsp_manager/plugin.py | 248 -------- 17 files changed, 207 insertions(+), 1788 deletions(-) delete mode 100644 plugins/gtksourceview/lsp_client/capabilities.py rename plugins/gtksourceview/{lsp_manager => lsp_client}/client_ipc.py (100%) rename plugins/gtksourceview/{lsp_manager => lsp_client}/config.json (52%) delete mode 100644 plugins/gtksourceview/lsp_client/lsp_controller.py rename plugins/gtksourceview/{lsp_manager => lsp_client}/lsp_message_structs.py (100%) delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/LICENSE delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/__init__.py delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/json_rpc_endpoint.py delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/lsp_client.py delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/lsp_endpoint.py delete mode 100644 plugins/gtksourceview/lsp_client/pylspclient/lsp_structs.py delete mode 100644 plugins/gtksourceview/lsp_manager/__init__.py delete mode 100644 plugins/gtksourceview/lsp_manager/__main__.py delete mode 100644 plugins/gtksourceview/lsp_manager/manifest.json delete mode 100644 plugins/gtksourceview/lsp_manager/plugin.py diff --git a/plugins/gtksourceview/lsp_client/capabilities.py b/plugins/gtksourceview/lsp_client/capabilities.py deleted file mode 100644 index 8cd31d2..0000000 --- a/plugins/gtksourceview/lsp_client/capabilities.py +++ /dev/null @@ -1,201 +0,0 @@ -# Python imports - -# Lib imports - -# Application imports - - - -class Capabilities: - data = { - "textDocument": { - "codeAction": { - "dynamicRegistration": True - }, - "codeLens": { - "dynamicRegistration": True - }, - "colorProvider": { - "dynamicRegistration": True - }, - "completion": { - "completionItem": { - "commitCharactersSupport": True, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "snippetSupport": True - }, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "contextSupport": True, - "dynamicRegistration": True - }, - "definition": { - "dynamicRegistration": True - }, - "documentHighlight": { - "dynamicRegistration": True - }, - "documentLink": { - "dynamicRegistration": True - }, - "documentSymbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - } - }, - "formatting": { - "dynamicRegistration": True - }, - "hover": { - "contentFormat": [ - "markdown", - "plaintext" - ], - "dynamicRegistration": True - }, - "implementation": { - "dynamicRegistration": True - }, - "onTypeFormatting": { - "dynamicRegistration": True - }, - "publishDiagnostics": { - "relatedInformation": True - }, - "rangeFormatting": { - "dynamicRegistration": True - }, - "references": { - "dynamicRegistration": True - }, - "rename": { - "dynamicRegistration": True - }, - "signatureHelp": { - "dynamicRegistration": True, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ] - } - }, - "synchronization": { - "didSave": True, - "dynamicRegistration": True, - "willSave": True, - "willSaveWaitUntil": True - }, - "typeDefinition": { - "dynamicRegistration": True - } - }, - "workspace": { - "applyEdit": True, - "configuration": True, - "didChangeConfiguration": { - "dynamicRegistration": True - }, - "didChangeWatchedFiles": { - "dynamicRegistration": True - }, - "executeCommand": { - "dynamicRegistration": True - }, - "symbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - } - }, - "workspaceEdit": { - "documentChanges": True - }, - "workspaceFolders": True - } -} \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_manager/client_ipc.py b/plugins/gtksourceview/lsp_client/client_ipc.py similarity index 100% rename from plugins/gtksourceview/lsp_manager/client_ipc.py rename to plugins/gtksourceview/lsp_client/client_ipc.py diff --git a/plugins/gtksourceview/lsp_manager/config.json b/plugins/gtksourceview/lsp_client/config.json similarity index 52% rename from plugins/gtksourceview/lsp_manager/config.json rename to plugins/gtksourceview/lsp_client/config.json index 8a9e0c4..ae53ba3 100644 --- a/plugins/gtksourceview/lsp_manager/config.json +++ b/plugins/gtksourceview/lsp_client/config.json @@ -1,4 +1,5 @@ { + "lsp_manager_start_command": ["python", "/opt/lsp-manager.zip"], "websocket": { "host": "localhost", "port": 8765 diff --git a/plugins/gtksourceview/lsp_client/lsp_controller.py b/plugins/gtksourceview/lsp_client/lsp_controller.py deleted file mode 100644 index 902b555..0000000 --- a/plugins/gtksourceview/lsp_client/lsp_controller.py +++ /dev/null @@ -1,187 +0,0 @@ -# Python imports -import subprocess -import threading - -# Lib imports -from . import pylspclient - -# Application imports -from .capabilities import Capabilities - - - -class ReadPipe(threading.Thread): - def __init__(self, pipe): - threading.Thread.__init__(self) - - self.daemon = True - self.pipe = pipe - - - def run(self): - line = self.pipe.readline().decode('utf-8') - while line: - line = self.pipe.readline().decode('utf-8') - - -class LSPController: - 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 = { - "window/showMessage": print, - "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 - - root_path = None - # root_uri = 'file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/Newton_Editor/src/' - # workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}] - root_uri = '' - workspace_folders = [{'name': '', 'uri': root_uri}] - - lsp_client = self._generate_client(language, server_proc) - lsp_client.initialize( - processId = server_proc.pid, \ - rootPath = root_path, \ - rootUri = root_uri, \ - initializationOptions = initialization_options, \ - capabilities = Capabilities.data, \ - trace = "off", \ - # trace = "on", \ - workspaceFolders = workspace_folders - ) - - lsp_client.initialized() - - return True - - def create_lsp_server(self, server_command: [] = []): - if not server_command: return None - - server_proc = subprocess.Popen(server_command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) - read_pipe = ReadPipe(server_proc.stderr) - read_pipe.start() - - return server_proc - - - 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) - ) - - return [] - - def do_change(self, uri, language_id, line, start, end, text): - if language_id in self.lsp_clients.keys(): - - start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset()) - end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset()) - range_info = pylspclient.lsp_structs.Range(start_pos, end_pos) - text_length = len(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( text_document, change_event ) - - return [] - - def do_completion(self, language_id, uri, line, offset, _char, is_invoked = False): - if language_id in self.lsp_clients.keys(): - trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerCharacter - - if _char in [".", " "]: - trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerCharacter - elif is_invoked: - trigger = pylspclient.lsp_structs.CompletionTriggerKind.Invoked - else: - trigger = pylspclient.lsp_structs.CompletionTriggerKind.TriggerForIncompleteCompletions - - return self.lsp_clients[language_id].completion( - pylspclient.lsp_structs.TextDocumentIdentifier(uri), - pylspclient.lsp_structs.Position(line, offset), - None - # pylspclient.lsp_structs.CompletionContext(trigger, _char) - ) - - return [] - - - def load_lsp_server(self, language_id): - if not language_id in self.lsp_servers_config.keys(): - return - - 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) - if self.create_client(language_id, server_proc, config_options): - return self.lsp_clients[language_id] - - return None - - 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) - ) \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_manager/lsp_message_structs.py b/plugins/gtksourceview/lsp_client/lsp_message_structs.py similarity index 100% rename from plugins/gtksourceview/lsp_manager/lsp_message_structs.py rename to plugins/gtksourceview/lsp_client/lsp_message_structs.py diff --git a/plugins/gtksourceview/lsp_client/manifest.json b/plugins/gtksourceview/lsp_client/manifest.json index 255c6d6..4078b00 100644 --- a/plugins/gtksourceview/lsp_client/manifest.json +++ b/plugins/gtksourceview/lsp_client/manifest.json @@ -3,10 +3,16 @@ "name": "LSP Client", "author": "ITDominator", "version": "0.0.1", - "credit": "Avi Yeger for the pylspclient used by this plugin. Link: https://github.com/yeger00/pylspclient", + "credit": "", "support": "", "requests": { - "pass_events": "true" + "pass_events": "true", + "pass_ui_objects": [ + "separator_right" + ], + "bind_keys": [ + "LSP Client Toggle||tggl_lsp_window:l" + ] } } -} \ No newline at end of file +} diff --git a/plugins/gtksourceview/lsp_client/plugin.py b/plugins/gtksourceview/lsp_client/plugin.py index b566e93..4def8c3 100644 --- a/plugins/gtksourceview/lsp_client/plugin.py +++ b/plugins/gtksourceview/lsp_client/plugin.py @@ -1,72 +1,183 @@ # Python imports -import os +import signal +import subprocess import json -import threading # Lib imports -from gi.repository import GLib +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk # Application imports - - from plugins.plugin_base import PluginBase -from .lsp_controller import LSPController - - - -class LSPPliginException(Exception): - ... +from .client_ipc import ClientIPC class Plugin(PluginBase): def __init__(self): - super().__init__() - 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 - 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.timer = None + super().__init__() + 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 + self.config_file = "config.json" + self.config: dict = {} + self.lsp_client_proc = None + self.lsp_window = None def generate_reference_ui_element(self): ... def run(self): - if os.path.exists(self.lsp_config_path): - with open(self.lsp_config_path, "r") as f: - self.lsp_servers_config = json.load(f) - else: - 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 + try: + with open(self.config_file) as f: + self.config = json.load(f) + except Exception as e: + raise Exception(f"Couldn't load config.json...\n{repr(e)}") + + self.lsp_window = Gtk.Window() + box1 = Gtk.Box() + box2 = Gtk.Box() + start_btn = Gtk.Button(label = "Start LSP Client") + stop_btn = Gtk.Button(label = "Stop LSP Client") + pid_label = Gtk.Label(label = "LSP PID: ") + + box1.set_orientation( Gtk.Orientation.VERTICAL ) + + self.lsp_window.set_deletable(False) + self.lsp_window.set_skip_pager_hint(True) + self.lsp_window.set_skip_taskbar_hint(True) + self.lsp_window.set_title("LSP Manager") + self.lsp_window.set_size_request(480, 320) + + start_btn.connect("clicked", self.start_lsp_manager) + stop_btn.connect("clicked", self.stop_lsp_manager) + + box1.add(pid_label) + box2.add(start_btn) + box2.add(stop_btn) + box1.add(box2) + self.lsp_window.add(box1) + + box1.show_all() + + self.inner_subscribe_to_events() + + def _shutting_down(self): + self.stop_lsp_manager() + + def _tear_down(self, widget, eve): + return True + + def _tggl_lsp_window(self, widget = None): + if not self.lsp_window.is_visible(): + self.lsp_window.show() + else: + self.lsp_window.hide() - if len(self.lsp_servers_config.keys()) > 0: - self.lsp_controller = LSPController(self.lsp_servers_config) - self.inner_subscribe_to_events() def subscribe_to_events(self): - ... - + self._event_system.subscribe("tggl_lsp_window", self._tggl_lsp_window) + def inner_subscribe_to_events(self): self._event_system.subscribe("shutting_down", self._shutting_down) - # self._event_system.subscribe("buffer_changed", self._buffer_changed) - self._event_system.subscribe("textDocument/didChange", self._buffer_changed) - 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) - self._event_system.subscribe("textDocument/completion", self._do_completion) + self._event_system.subscribe("textDocument/didOpen", self._lsp_did_open) + self._event_system.subscribe("textDocument/didSave", self._lsp_did_save) + self._event_system.subscribe("textDocument/didClose", self._lsp_did_close) + self._event_system.subscribe("textDocument/didChange", self._lsp_did_change) + self._event_system.subscribe("textDocument/definition", self._lsp_goto) + self._event_system.subscribe("textDocument/completion", self._lsp_completion) - def _shutting_down(self): - self.lsp_controller._shutting_down() + def start_lsp_manager(self, button): + if self.lsp_client_proc: return + self.lsp_client_proc = subprocess.Popen(self.config["lsp_manager_start_command"]) + self._load_client_ipc_server() + + def _load_client_ipc_server(self): + self.client_ipc = ClientIPC() + self.client_ipc.set_event_system(self._event_system) + self._ipc_realization_check(self.client_ipc) + + if not self.client_ipc.is_ipc_alive: + raise AppLaunchException(f"LSP IPC Server Already Exists...") + + def _ipc_realization_check(self, ipc_server): + try: + ipc_server.create_ipc_listener() + except Exception: + ipc_server.send_test_ipc_message() + + try: + ipc_server.create_ipc_listener() + except Exception as e: + ... + + def stop_lsp_manager(self, button = None): + if not self.lsp_client_proc: return + if not self.lsp_client_proc.poll() is None: + self.lsp_client_proc = None + return + + self.lsp_client_proc.terminate() + self.client_ipc.is_ipc_alive = False + self.lsp_client_proc = None + + def _lsp_did_open(self, language_id: str, uri: str, text: str): + if not self.lsp_client_proc: return + + data = { + "method": "textDocument/didOpen", + "language_id": language_id, + "uri": uri, + "version": -1, + "text": text, + "line": -1, + "column": -1, + "char": "" + } + + self.send_message(data) + + def _lsp_did_save(self, uri: str, text: str): + if not self.lsp_client_proc: return + + data = { + "method": "textDocument/didSave", + "language_id": "", + "uri": uri, + "version": -1, + "text": text, + "line": -1, + "column": -1, + "char": "" + } + + self.send_message(data) + + def _lsp_did_close(self, uri: str): + if not self.lsp_client_proc: return + + data = { + "method": "textDocument/didClose", + "language_id": "", + "uri": uri, + "version": -1, + "text": "", + "line": -1, + "column": -1, + "char": "" + } + + self.send_message(data) + + def _lsp_did_change(self, language_id: str, uri: str, buffer): + if not self.lsp_client_proc: return - def _buffer_changed(self, file_type, buffer): iter = buffer.get_iter_at_mark( buffer.get_insert() ) line = iter.get_line() + column = iter.get_line_offset() start = iter.copy() end = iter.copy() @@ -75,12 +186,39 @@ class Plugin(PluginBase): end.forward_to_line_end() text = buffer.get_text(start, end, include_hidden_chars = False) - result = self.lsp_controller.do_change(buffer.uri, buffer.language_id, line, start, end, text) + data = { + "method": "textDocument/didChange", + "language_id": language_id, + "uri": uri, + "version": buffer.version_id, + "text": text, + "line": line, + "column": column, + "char": "" + } + self.send_message(data) + + def _lsp_goto(self, language_id: str, uri: str, line: int, column: int): + if not self.lsp_client_proc: return + + data = { + "method": "textDocument/definition", + "language_id": language_id, + "uri": uri, + "version": -1, + "text": "", + "line": line, + "column": column, + "char": "" + } + + self.send_message(data) + + def _lsp_completion(self, source_view): + if not self.lsp_client_proc: return - def _do_completion(self, source_view): filepath = source_view.get_current_file() - if not filepath: return uri = filepath.get_uri() @@ -88,27 +226,23 @@ class Plugin(PluginBase): iter = buffer.get_iter_at_mark( buffer.get_insert() ) line = iter.get_line() - _char = iter.get_char() + char = iter.get_char() if iter.backward_char(): - _char = iter.get_char() + char = iter.get_char() - offset = iter.get_line_offset() - result = self.lsp_controller.do_completion( - source_view.get_filetype(), - uri, - line, - offset, - _char - ) + column = iter.get_line_offset() + data = { + "method": "textDocument/completion", + "language_id": source_view.get_filetype(), + "uri": uri, + "version": source_view.get_version_id(), + "text": "", + "line": line, + "column": column, + "char": char + } - return result + self.send_message(data) - def _do_goto(self, language_id, uri, line, offset): - results = self.lsp_controller.do_goto(language_id, uri, line, offset) - - if len(results) == 1: - result = results[0] - file = result.uri[7:] - line = result.range.end.line - message = f"FILE|{file}:{line}" - self._event_system.emit("post_file_to_ipc", message) \ No newline at end of file + def send_message(self, data: dict): + self.client_ipc.send_manager_ipc_message( json.dumps(data) ) diff --git a/plugins/gtksourceview/lsp_client/pylspclient/LICENSE b/plugins/gtksourceview/lsp_client/pylspclient/LICENSE deleted file mode 100644 index 0728ddb..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Avi Yeger - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/plugins/gtksourceview/lsp_client/pylspclient/__init__.py b/plugins/gtksourceview/lsp_client/pylspclient/__init__.py deleted file mode 100644 index 58572a1..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -__all__ = [] - -from .json_rpc_endpoint import JsonRpcEndpoint -from .lsp_client import LspClient -from .lsp_endpoint import LspEndpoint -from . import lsp_structs \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_client/pylspclient/json_rpc_endpoint.py b/plugins/gtksourceview/lsp_client/pylspclient/json_rpc_endpoint.py deleted file mode 100644 index 30561bb..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/json_rpc_endpoint.py +++ /dev/null @@ -1,105 +0,0 @@ -from __future__ import print_function - -import threading -import json - -from . import lsp_structs - - - -JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}" -LEN_HEADER = "Content-Length: " -TYPE_HEADER = "Content-Type: " - - -# TODO: add content-type - - -class MyEncoder(json.JSONEncoder): - """ - Encodes an object in JSON - """ - - def default(self, o): # pylint: disable=E0202 - return o.__dict__ - - -class JsonRpcEndpoint(object): - ''' - Thread safe JSON RPC endpoint implementation. Responsible to recieve and - send JSON RPC messages, as described in the protocol. More information can - be found: https://www.jsonrpc.org/ - ''' - - def __init__(self, stdin, stdout): - self.stdin = stdin - self.stdout = stdout - self.read_lock = threading.Lock() - self.write_lock = threading.Lock() - - @staticmethod - def __add_header(json_string): - ''' - Adds a header for the given json string - - :param str json_string: The string - :return: the string with the header - ''' - return JSON_RPC_REQ_FORMAT.format(json_string_len = len(json_string), json_string = json_string) - - def send_request(self, message): - ''' - Sends the given message. - - :param dict message: The message to send. - ''' - json_string = json.dumps(message, cls = MyEncoder) - jsonrpc_req = self.__add_header(json_string) - with self.write_lock: - self.stdin.write(jsonrpc_req.encode()) - self.stdin.flush() - - def recv_response(self): - ''' - Recives a message. - - :return: a message - ''' - with self.read_lock: - message_size = None - while True: - # read header - line = self.stdout.readline() - if not line: - # server quit - return None - line = line.decode("utf-8") - if not line.endswith("\r\n"): - raise lsp_structs.ResponseError( - lsp_structs.ErrorCodes.ParseError, - "Bad header: missing newline") - # remove the "\r\n" - line = line[:-2] - if line == "": - # done with the headers - break - elif line.startswith(LEN_HEADER): - line = line[len(LEN_HEADER):] - if not line.isdigit(): - raise lsp_structs.ResponseError( - lsp_structs.ErrorCodes.ParseError, - "Bad header: size is not int") - message_size = int(line) - elif line.startswith(TYPE_HEADER): - ... - else: - raise lsp_structs.ResponseError( - lsp_structs.ErrorCodes.ParseError, - "Bad header: unkown header") - if not message_size: - raise lsp_structs.ResponseError( - lsp_structs.ErrorCodes.ParseError, - "Bad header: missing size") - - jsonrpc_res = self.stdout.read(message_size).decode("utf-8") - return json.loads(jsonrpc_res) \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_client/pylspclient/lsp_client.py b/plugins/gtksourceview/lsp_client/pylspclient/lsp_client.py deleted file mode 100644 index ec42b5e..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/lsp_client.py +++ /dev/null @@ -1,257 +0,0 @@ -# Python imports - -# Lib imports - -# Application imports -from . import lsp_structs - - - -class LspClient(object): - def __init__(self, lsp_endpoint): - """ - Constructs a new LspClient instance. - - :param lsp_endpoint: TODO - """ - - self.lsp_endpoint = lsp_endpoint - - def initialize(self, processId, rootPath, rootUri, initializationOptions, capabilities, trace, workspaceFolders): - """ - The initialize request is sent as the first request from the client to the server. If the server receives a request or notification - before the initialize request it should act as follows: - - 1. For a request the response should be an error with code: -32002. The message can be picked by the server. - 2. Notifications should be dropped, except for the exit notification. This will allow the exit of a server without an initialize request. - - Until the server has responded to the initialize request with an InitializeResult, the client must not send any additional requests or - notifications to the server. In addition the server is not allowed to send any requests or notifications to the client until it has responded - with an InitializeResult, with the exception that during the initialize request the server is allowed to send the notifications window/showMessage, - window/logMessage and telemetry/event as well as the window/showMessageRequest request to the client. - - The initialize request may only be sent once. - - :param int processId: The process Id of the parent process that started the server. Is null if the process has not been started by another process. - If the parent process is not alive then the server should exit (see exit notification) its process. - :param str rootPath: The rootPath of the workspace. Is null if no folder is open. Deprecated in favour of rootUri. - :param DocumentUri rootUri: The rootUri of the workspace. Is null if no folder is open. If both `rootPath` and `rootUri` are set - `rootUri` wins. - :param any initializationOptions: User provided initialization options. - :param ClientCapabilities capabilities: The capabilities provided by the client (editor or tool). - :param Trace trace: The initial trace setting. If omitted trace is disabled ('off'). - :param list workspaceFolders: The workspace folders configured in the client when the server starts. This property is only available if the client supports workspace folders. - It can be `null` if the client supports workspace folders but none are configured. - """ - - self.lsp_endpoint.start() - return self.lsp_endpoint.call_method("initialize", - processId = processId, - rootPath = rootPath, - rootUri = rootUri, - initializationOptions = initializationOptions, - capabilities = capabilities, - trace = trace, - workspaceFolders = workspaceFolders - ) - - def initialized(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("initialized") - - def shutdown(self): - """ - """ - - return self.lsp_endpoint.call_method("shutdown") - - def exit(self): - """ - """ - - self.lsp_endpoint.send_notification("exit") - self.lsp_endpoint.stop() - - - def didOpen(self, textDocument): - """ - The document open notification is sent from the client to the server to signal newly opened text documents. The document's truth is - now managed by the client and the server must not try to read the document's truth using the document's uri. Open in this sense - means it is managed by the client. It doesn't necessarily mean that its content is presented in an editor. An open notification must - not be sent more than once without a corresponding close notification send before. This means open and close notification must be - balanced and the max open count for a particular textDocument is one. Note that a server's ability to fulfill requests is independent - of whether a text document is open or closed. - - The DidOpenTextDocumentParams contain the language id the document is associated with. If the language Id of a document changes, the - client needs to send a textDocument/didClose to the server followed by a textDocument/didOpen with the new language id if the server - handles the new language id as well. - - :param TextDocumentItem textDocument: The document that was opened. - """ - - 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): - """ - 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. - - :param TextDocumentItem textDocument: The text document. - :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 S' and c2 to S''. - """ - - return self.lsp_endpoint.send_notification("textDocument/didChange", textDocument = textDocument, contentChanges = contentChanges) - - def documentSymbol(self, textDocument): - """ - The document symbol request is sent from the client to the server to - return a flat list of all symbols found in a given text document. - Neither the symbol's location range nor the symbol's container name - should be used to infer a hierarchy. - - :param TextDocumentItem textDocument: The text document. - """ - result_dict = self.lsp_endpoint.call_method( "textDocument/documentSymbol", textDocument=textDocument ) - - if not result_dict: return [] - return [lsp_structs.SymbolInformation(**sym) for sym in result_dict] - - def declaration(self, textDocument, position): - """ - The go to declaration request is sent from the client to the server to - resolve the declaration location of a symbol at a given text document - position. - - The result type LocationLink[] got introduce with version 3.14.0 and - depends in the corresponding client capability - `clientCapabilities.textDocument.declaration.linkSupport`. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - """ - - result_dict = self.lsp_endpoint.call_method("textDocument/declaration", - textDocument = textDocument, - position = position - ) - - if not result_dict: return [] - - if "uri" in result_dict: - return lsp_structs.Location(**result_dict) - - return [lsp_structs.Location(**loc) if "uri" in loc else lsp_structs.LinkLocation(**loc) for loc in result_dict] - - def definition(self, textDocument, position): - """ - The goto definition request is sent from the client to the server to - resolve the definition location of a symbol at a given text document - position. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - """ - - result_dict = self.lsp_endpoint.call_method("textDocument/definition", - textDocument = textDocument, - position = position - ) - - if not result_dict: return [] - return [lsp_structs.Location(**loc) for loc in result_dict] - - def typeDefinition(self, textDocument, position): - """ - The goto type definition request is sent from the client to the server - to resolve the type definition location of a symbol at a given text - document position. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - """ - - result_dict = self.lsp_endpoint.call_method("textDocument/typeDefinition", - textDocument = textDocument, - position = position - ) - - if not result_dict: return [] - return [lsp_structs.Location(**loc) for loc in result_dict] - - def signatureHelp(self, textDocument, position): - """ - The signature help request is sent from the client to the server to - request signature information at a given cursor position. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - """ - - result_dict = self.lsp_endpoint.call_method( "textDocument/signatureHelp", - textDocument = textDocument, - position = position - ) - - if not result_dict: return [] - return lsp_structs.SignatureHelp(**result_dict) - - def completion(self, textDocument, position, context): - """ - The signature help request is sent from the client to the server to - request signature information at a given cursor position. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - :param CompletionContext context: The completion context. This is only - available if the client specifies - to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` - """ - - result_dict = self.lsp_endpoint.call_method("textDocument/completion", - textDocument = textDocument, - position = position, - context = context - ) - if not result_dict: return [] - - if "isIncomplete" in result_dict: - return lsp_structs.CompletionList(**result_dict) - - return [lsp_structs.CompletionItem(**loc) for loc in result_dict] - - def references(self, textDocument, position): - """ - The references request is sent from the client to the server to resolve - project-wide references for the symbol denoted by the given text - document position. - - :param TextDocumentItem textDocument: The text document. - :param Position position: The position inside the text document. - """ - - result_dict = self.lsp_endpoint.call_method("textDocument/references", - textDocument = textDocument, - position = position) - - if not result_dict: return [] - return [lsp_structs.Location(**loc) for loc in result_dict] \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_client/pylspclient/lsp_endpoint.py b/plugins/gtksourceview/lsp_client/pylspclient/lsp_endpoint.py deleted file mode 100644 index 655833e..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/lsp_endpoint.py +++ /dev/null @@ -1,111 +0,0 @@ -from __future__ import print_function - -import threading - -from . import lsp_structs - - - -class LspEndpoint(threading.Thread): - def __init__(self, json_rpc_endpoint, method_callbacks = {}, notify_callbacks = {}, timeout = 2): - threading.Thread.__init__(self) - self.json_rpc_endpoint = json_rpc_endpoint - self.notify_callbacks = notify_callbacks - self.method_callbacks = method_callbacks - self.event_dict = {} - self.response_dict = {} - self.next_id = 0 - self._timeout = timeout - self.shutdown_flag = False - - def handle_result(self, rpc_id, result, error): - self.response_dict[rpc_id] = (result, error) - cond = self.event_dict[rpc_id] - cond.acquire() - cond.notify() - cond.release() - - def stop(self): - self.shutdown_flag = True - - def run(self): - while not self.shutdown_flag: - try: - jsonrpc_message = self.json_rpc_endpoint.recv_response() - if jsonrpc_message is None: break - - method = jsonrpc_message.get("method") - result = jsonrpc_message.get("result") - error = jsonrpc_message.get("error") - rpc_id = jsonrpc_message.get("id") - params = jsonrpc_message.get("params") - - if method: - if rpc_id is not None: - if method not in self.method_callbacks: - raise lsp_structs.ResponseError( - lsp_structs.ErrorCodes.MethodNotFound, - "Method not found: {method}" - .format(method=method)) - result = self.method_callbacks[method](params) - self.send_response(rpc_id, result, None) - else: - if method not in self.notify_callbacks: - print("Notify method not found: {method}.".format(method=method)) - else: - self.notify_callbacks[method](params) - else: - self.handle_result(rpc_id, result, error) - except lsp_structs.ResponseError as e: - self.send_response(rpc_id, None, e) - - def send_response(self, id, result, error): - message_dict = {} - message_dict["jsonrpc"] = "2.0" - message_dict["id"] = id - - if result: - message_dict["result"] = result - if error: - message_dict["error"] = error - - self.json_rpc_endpoint.send_request(message_dict) - - def send_message(self, method_name, params, id=None): - message_dict = {} - message_dict["jsonrpc"] = "2.0" - - if id is not None: - message_dict["id"] = id - - message_dict["method"] = method_name - message_dict["params"] = params - - self.json_rpc_endpoint.send_request(message_dict) - - def call_method(self, method_name, **kwargs): - current_id = self.next_id - self.next_id += 1 - cond = threading.Condition() - self.event_dict[current_id] = cond - - cond.acquire() - self.send_message(method_name, kwargs, current_id) - if self.shutdown_flag: - return None - - if not cond.wait(timeout=self._timeout): - raise TimeoutError() - cond.release() - - self.event_dict.pop(current_id) - result, error = self.response_dict.pop(current_id) - if error: - raise lsp_structs.ResponseError(error.get("code"), - error.get("message"), - error.get("data")) - - return result - - def send_notification(self, method_name, **kwargs): - self.send_message(method_name, kwargs) \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_client/pylspclient/lsp_structs.py b/plugins/gtksourceview/lsp_client/pylspclient/lsp_structs.py deleted file mode 100644 index 70804a5..0000000 --- a/plugins/gtksourceview/lsp_client/pylspclient/lsp_structs.py +++ /dev/null @@ -1,566 +0,0 @@ -import enum - - - -def to_type(o, new_type): - ''' - Helper funciton that receives an object or a dict and convert it to a new - given type. - - :param object|dict o: The object to convert - :param Type new_type: The type to convert to. - ''' - - return o if new_type == type(o) else new_type(**o) - - -class Position(object): - def __init__(self, line, character): - """ - Constructs a new Position instance. - - :param int line: Line position in a document (zero-based). - :param int character: Character offset on a line in a document - (zero-based). - """ - self.line = line - self.character = character - - -class Range(object): - def __init__(self, start, end): - """ - Constructs a new Range instance. - - :param Position start: The range's start position. - :param Position end: The range's end position. - """ - self.start = to_type(start, Position) - self.end = to_type(end, Position) - - -class Location(object): - """ - Represents a location inside a resource, such as a line inside a text file. - """ - - def __init__(self, uri, range): - """ - Constructs a new Location instance. - - :param str uri: Resource file. - :param Range range: The range inside the file - """ - self.uri = uri - self.range = to_type(range, Range) - - -class LocationLink(object): - """ - Represents a link between a source and a target location. - """ - - def __init__(self, originSelectionRange, targetUri, targetRange, targetSelectionRange): - """ - Constructs a new LocationLink instance. - - :param Range originSelectionRange: Span of the origin of this link. - Used as the underlined span for mouse interaction. Defaults to the word range at the mouse position. - :param str targetUri: The target resource identifier of this link. - :param Range targetRange: The full target range of this link. If the target for example is a symbol then target - range is the range enclosing this symbol not including leading/trailing whitespace but everything else - like comments. This information is typically used to highlight the range in the editor. - :param Range targetSelectionRange: The range that should be selected and revealed when this link is being followed, - e.g the name of a function. Must be contained by the the `targetRange`. See also `DocumentSymbol#range` - """ - self.originSelectionRange = to_type(originSelectionRange, Range) - self.targetUri = targetUri - self.targetRange = to_type(targetRange, Range) - self.targetSelectionRange = to_type(targetSelectionRange, Range) - - -class Diagnostic(object): - def __init__(self, range, severity, code, source, message, relatedInformation): - """ - Constructs a new Diagnostic instance. - :param Range range: The range at which the message applies.Resource file. - :param int severity: The diagnostic's severity. Can be omitted. If omitted it is up to the - client to interpret diagnostics as error, warning, info or hint. - :param str code: The diagnostic's code, which might appear in the user interface. - :param str source: A human-readable string describing the source of this - diagnostic, e.g. 'typescript' or 'super lint'. - :param str message: The diagnostic's message. - :param list relatedInformation: An array of related diagnostic information, e.g. when symbol-names within - a scope collide all definitions can be marked via this property. - """ - self.range = range - self.severity = severity - self.code = code - self.source = source - self.message = message - self.relatedInformation = relatedInformation - - -class DiagnosticSeverity(object): - Error = 1 - Warning = 2 # TODO: warning is known in python - Information = 3 - Hint = 4 - - -class DiagnosticRelatedInformation(object): - def __init__(self, location, message): - """ - Constructs a new Diagnostic instance. - :param Location location: The location of this related diagnostic information. - :param str message: The message of this related diagnostic information. - """ - self.location = location - self.message = message - - -class Command(object): - def __init__(self, title, command, arguments): - """ - Constructs a new Diagnostic instance. - :param str title: Title of the command, like `save`. - :param str command: The identifier of the actual command handler. - :param list argusments: Arguments that the command handler should be invoked with. - """ - self.title = title - self.command = command - self.arguments = arguments - - -class TextDocumentItem(object): - """ - An item to transfer a text document from the client to the server. - """ - def __init__(self, uri, languageId, version, text): - """ - Constructs a new Diagnostic instance. - - :param DocumentUri uri: Title of the command, like `save`. - :param str languageId: The identifier of the actual command handler. - :param int version: Arguments that the command handler should be invoked with. - :param str text: Arguments that the command handler should be invoked with. - """ - self.uri = uri - self.languageId = languageId - self.version = version - self.text = text - - -class TextDocumentIdentifier(object): - """ - Text documents are identified using a URI. On the protocol level, URIs are passed as strings. - """ - def __init__(self, uri): - """ - Constructs a new TextDocumentIdentifier instance. - - :param DocumentUri uri: The text document's URI. - """ - self.uri = uri - - -class VersionedTextDocumentIdentifier(TextDocumentIdentifier): - """ - An identifier to denote a specific version of a text document. - """ - def __init__(self, uri, version): - """ - Constructs a new TextDocumentIdentifier instance. - - :param DocumentUri uri: The text document's URI. - :param int version: The version number of this document. If a versioned - text document identifier is sent from the server to the client and - the file is not open in the editor (the server has not received an - open notification before) the server can send `null` to indicate - that the version is known and the content on disk is the truth (as - speced with document content ownership). - The version number of a document will increase after each change, including - undo/redo. The number doesn't need to be consecutive. - """ - super(VersionedTextDocumentIdentifier, self).__init__(uri) - self.version = version - - -class TextDocumentContentChangeEvent(object): - """ - An event describing a change to a text document. If range and rangeLength are omitted - the new text is considered to be the full content of the document. - """ - def __init__(self, range, rangeLength, text): - """ - Constructs a new TextDocumentContentChangeEvent instance. - - :param Range range: The range of the document that changed. - :param int rangeLength: The length of the range that got replaced. - :param str text: The new text of the range/document. - """ - self.range = range - self.rangeLength = rangeLength - self.text = text - - -class TextDocumentPositionParams(object): - """ - A parameter literal used in requests to pass a text document and a position inside that document. - """ - def __init__(self, textDocument, position): - """ - Constructs a new TextDocumentPositionParams instance. - - :param TextDocumentIdentifier textDocument: The text document. - :param Position position: The position inside the text document. - """ - self.textDocument = textDocument - self.position = position - - -class LANGUAGE_IDENTIFIER(object): - BAT = "bat" - BIBTEX = "bibtex" - CLOJURE = "clojure" - COFFESCRIPT = "coffeescript" - C = "c" - CPP = "cpp" - CSHARP = "csharp" - CSS = "css" - DIFF = "diff" - DOCKERFILE = "dockerfile" - FSHARP = "fsharp" - GIT_COMMIT = "git-commit" - GIT_REBASE = "git-rebase" - GO = "go" - GROOVY = "groovy" - HANDLEBARS = "handlebars" - HTML = "html" - INI = "ini" - JAVA = "java" - JAVASCRIPT = "javascript" - JSON = "json" - LATEX = "latex" - LESS = "less" - LUA = "lua" - MAKEFILE = "makefile" - MARKDOWN = "markdown" - OBJECTIVE_C = "objective-c" - OBJECTIVE_CPP = "objective-cpp" - Perl = "perl" - PHP = "php" - POWERSHELL = "powershell" - PUG = "jade" - PYTHON = "python" - R = "r" - RAZOR = "razor" - RUBY = "ruby" - RUST = "rust" - SASS = "sass" - SCSS = "scss" - ShaderLab = "shaderlab" - SHELL_SCRIPT = "shellscript" - SQL = "sql" - SWIFT = "swift" - TYPE_SCRIPT = "typescript" - TEX = "tex" - VB = "vb" - XML = "xml" - XSL = "xsl" - YAML = "yaml" - - -class SymbolKind(enum.Enum): - File = 1 - Module = 2 - Namespace = 3 - Package = 4 - Class = 5 - Method = 6 - Property = 7 - Field = 8 - Constructor = 9 - Enum = 10 - Interface = 11 - Function = 12 - Variable = 13 - Constant = 14 - String = 15 - Number = 16 - Boolean = 17 - Array = 18 - Object = 19 - Key = 20 - Null = 21 - EnumMember = 22 - Struct = 23 - Event = 24 - Operator = 25 - TypeParameter = 26 - - -class SymbolInformation(object): - """ - Represents information about programming constructs like variables, classes, interfaces etc. - """ - def __init__(self, name, kind, location, containerName = None, deprecated = False): - """ - Constructs a new SymbolInformation instance. - - :param str name: The name of this symbol. - :param int kind: The kind of this symbol. - :param bool Location: The location of this symbol. The location's range is used by a tool - to reveal the location in the editor. If the symbol is selected in the - tool the range's start information is used to position the cursor. So - the range usually spans more then the actual symbol's name and does - normally include things like visibility modifiers. - - The range doesn't have to denote a node range in the sense of a abstract - syntax tree. It can therefore not be used to re-construct a hierarchy of - the symbols. - :param str containerName: The name of the symbol containing this symbol. This information is for - user interface purposes (e.g. to render a qualifier in the user interface - if necessary). It can't be used to re-infer a hierarchy for the document - symbols. - :param bool deprecated: Indicates if this symbol is deprecated. - """ - self.name = name - self.kind = SymbolKind(kind) - self.deprecated = deprecated - self.location = to_type(location, Location) - self.containerName = containerName - - -class ParameterInformation(object): - """ - Represents a parameter of a callable-signature. A parameter can - have a label and a doc-comment. - """ - def __init__(self, label, documentation = ""): - """ - Constructs a new ParameterInformation instance. - - :param str label: The label of this parameter. Will be shown in the UI. - :param str documentation: The human-readable doc-comment of this parameter. Will be shown in the UI but can be omitted. - """ - self.label = label - self.documentation = documentation - - -class SignatureInformation(object): - """ - Represents the signature of something callable. A signature - can have a label, like a function-name, a doc-comment, and - a set of parameters. - """ - def __init__(self, label, documentation = "", parameters = []): - """ - Constructs a new SignatureInformation instance. - - :param str label: The label of this signature. Will be shown in the UI. - :param str documentation: The human-readable doc-comment of this signature. Will be shown in the UI but can be omitted. - :param ParameterInformation[] parameters: The parameters of this signature. - """ - self.label = label - self.documentation = documentation - self.parameters = [to_type(parameter, ParameterInformation) for parameter in parameters] - - -class SignatureHelp(object): - """ - Signature help represents the signature of something - callable. There can be multiple signature but only one - active and only one active parameter. - """ - def __init__(self, signatures, activeSignature = 0, activeParameter = 0): - """ - Constructs a new SignatureHelp instance. - - :param SignatureInformation[] signatures: One or more signatures. - :param int activeSignature: - :param int activeParameter: - """ - self.signatures = [to_type(signature, SignatureInformation) for signature in signatures] - self.activeSignature = activeSignature - self.activeParameter = activeParameter - - -class CompletionTriggerKind(object): - Invoked = 1 - TriggerCharacter = 2 - TriggerForIncompleteCompletions = 3 - - -class CompletionContext(object): - """ - Contains additional information about the context in which a completion request is triggered. - """ - def __init__(self, triggerKind, triggerCharacter = None): - """ - Constructs a new CompletionContext instance. - - :param CompletionTriggerKind triggerKind: How the completion was triggered. - :param str triggerCharacter: The trigger character (a single character) that has trigger code complete. - Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` - """ - self.triggerKind = triggerKind - if triggerCharacter: - self.triggerCharacter = triggerCharacter - - -class TextEdit(object): - """ - A textual edit applicable to a text document. - """ - def __init__(self, range, newText): - """ - :param Range range: The range of the text document to be manipulated. To insert - text into a document create a range where start === end. - :param str newText: The string to be inserted. For delete operations use an empty string. - """ - self.range = range - self.newText = newText - - -class InsertTextFormat(object): - PlainText = 1 - Snippet = 2 - - -class CompletionItem(object): - """ - """ - def __init__(self, label, \ - kind = None, \ - detail = None, \ - documentation = None, \ - deprecated = None, \ - preselect = None, \ - sortText = None, \ - filterText = None, \ - insertText = None, \ - insertTextFormat = None, \ - textEdit = None, \ - additionalTextEdits = None, \ - commitCharacters = None, \ - command = None, \ - data = None, \ - score = 0.0 - ): - """ - :param str label: The label of this completion item. By default also the text that is inserted when selecting - this completion. - :param int kind: The kind of this completion item. Based of the kind an icon is chosen by the editor. - :param str detail: A human-readable string with additional information about this item, like type or symbol information. - :param tr ocumentation: A human-readable string that represents a doc-comment. - :param bool deprecated: Indicates if this item is deprecated. - :param bool preselect: Select this item when showing. Note: that only one completion item can be selected and that the - tool / client decides which item that is. The rule is that the first item of those that match best is selected. - :param str sortText: A string that should be used when comparing this item with other items. When `falsy` the label is used. - :param str filterText: A string that should be used when filtering a set of completion items. When `falsy` the label is used. - :param str insertText: A string that should be inserted into a document when selecting this completion. When `falsy` the label is used. - The `insertText` is subject to interpretation by the client side. Some tools might not take the string literally. For example - VS Code when code complete is requested in this example `con` and a completion item with an `insertText` of `console` is provided it - will only insert `sole`. Therefore it is recommended to use `textEdit` instead since it avoids additional client side interpretation. - @deprecated Use textEdit instead. - :param InsertTextFormat insertTextFormat: The format of the insert text. The format applies to both the `insertText` property - and the `newText` property of a provided `textEdit`. - :param TextEdit textEdit: An edit which is applied to a document when selecting this completion. When an edit is provided the value of `insertText` is ignored. - Note:* The range of the edit must be a single line range and it must contain the position at which completion - has been requested. - :param TextEdit additionalTextEdits: An optional array of additional text edits that are applied when selecting this completion. - Edits must not overlap (including the same insert position) with the main edit nor with themselves. - Additional text edits should be used to change text unrelated to the current cursor position - (for example adding an import statement at the top of the file if the completion item will - insert an unqualified type). - :param str commitCharacters: An optional set of characters that when pressed while this completion is active will accept it first and - then type that character. *Note* that all commit characters should have `length=1` and that superfluous - characters will be ignored. - :param Command command: An optional command that is executed *after* inserting this completion. Note: that - additional modifications to the current document should be described with the additionalTextEdits-property. - :param data: An data entry field that is preserved on a completion item between a completion and a completion resolve request. - :param float score: Score of the code completion item. - """ - self.label = label - self.kind = kind - self.detail = detail - self.documentation = documentation - self.deprecated = deprecated - self.preselect = preselect - self.sortText = sortText - self.filterText = filterText - self.insertText = insertText - self.insertTextFormat = insertTextFormat - self.textEdit = textEdit - self.additionalTextEdits = additionalTextEdits - self.commitCharacters = commitCharacters - self.command = command - self.data = data - self.score = score - - -class CompletionItemKind(enum.Enum): - Text = 1 - Method = 2 - Function = 3 - Constructor = 4 - Field = 5 - Variable = 6 - Class = 7 - Interface = 8 - Module = 9 - Property = 10 - Unit = 11 - Value = 12 - Enum = 13 - Keyword = 14 - Snippet = 15 - Color = 16 - File = 17 - Reference = 18 - Folder = 19 - EnumMember = 20 - Constant = 21 - Struct = 22 - Event = 23 - Operator = 24 - TypeParameter = 25 - - -class CompletionList(object): - """ - Represents a collection of [completion items](#CompletionItem) to be preselect in the editor. - """ - def __init__(self, isIncomplete, items): - """ - Constructs a new CompletionContext instance. - - :param bool isIncomplete: This list it not complete. Further typing should result in recomputing this list. - :param CompletionItem items: The completion items. - """ - self.isIncomplete = isIncomplete - self.items = [to_type(i, CompletionItem) for i in items] - -class ErrorCodes(enum.Enum): - # Defined by JSON RPC - ParseError = -32700 - InvalidRequest = -32600 - MethodNotFound = -32601 - InvalidParams = -32602 - InternalError = -32603 - serverErrorStart = -32099 - serverErrorEnd = -32000 - ServerNotInitialized = -32002 - UnknownErrorCode = -32001 - - # Defined by the protocol. - RequestCancelled = -32800 - ContentModified = -32801 - -class ResponseError(Exception): - def __init__(self, code, message, data = None): - self.code = code - self.message = message - if data: - self.data = data \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_manager/__init__.py b/plugins/gtksourceview/lsp_manager/__init__.py deleted file mode 100644 index d36fa8c..0000000 --- a/plugins/gtksourceview/lsp_manager/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" - Pligin Module -""" diff --git a/plugins/gtksourceview/lsp_manager/__main__.py b/plugins/gtksourceview/lsp_manager/__main__.py deleted file mode 100644 index a576329..0000000 --- a/plugins/gtksourceview/lsp_manager/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" - Pligin Package -""" diff --git a/plugins/gtksourceview/lsp_manager/manifest.json b/plugins/gtksourceview/lsp_manager/manifest.json deleted file mode 100644 index 24671df..0000000 --- a/plugins/gtksourceview/lsp_manager/manifest.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "manifest": { - "name": "LSP Manager", - "author": "ITDominator", - "version": "0.0.1", - "credit": "", - "support": "", - "requests": { - "pass_events": "true", - "pass_ui_objects": ["separator_right"], - "bind_keys": ["LSP Manager Toggle||tggl_lsp_window:l"] - } - } -} \ No newline at end of file diff --git a/plugins/gtksourceview/lsp_manager/plugin.py b/plugins/gtksourceview/lsp_manager/plugin.py deleted file mode 100644 index 84a5de8..0000000 --- a/plugins/gtksourceview/lsp_manager/plugin.py +++ /dev/null @@ -1,248 +0,0 @@ -# Python imports -import signal -import subprocess -import json - -# Lib imports -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import Gtk - -# Application imports -from plugins.plugin_base import PluginBase -from .client_ipc import ClientIPC - - - -class Plugin(PluginBase): - def __init__(self): - - super().__init__() - self.name = "LSP Manager" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus - # where self.name should not be needed for message comms - self.ws_config_file = "config.json" - self.ws_config: dict = {} - self.lsp_manager_proc = None - self.lsp_window = None - - - def generate_reference_ui_element(self): - ... - - def run(self): - try: - with open(self.ws_config_file) as f: - self.ws_config = json.load(f)["websocket"] - except Exception as e: - raise Exception(f"Couldn't load config.json...\n{repr(e)}") - - self.lsp_window = Gtk.Window() - box1 = Gtk.Box() - box2 = Gtk.Box() - start_btn = Gtk.Button(label = "Start LSP Manager") - stop_btn = Gtk.Button(label = "Stop LSP Manager") - pid_label = Gtk.Label(label = "LSP PID: ") - - box1.set_orientation( Gtk.Orientation.VERTICAL ) - - self.lsp_window.set_deletable(False) - self.lsp_window.set_skip_pager_hint(True) - self.lsp_window.set_skip_taskbar_hint(True) - self.lsp_window.set_title("LSP Manager") - self.lsp_window.set_size_request(480, 320) - - start_btn.connect("clicked", self.start_lsp_manager) - stop_btn.connect("clicked", self.stop_lsp_manager) - - box1.add(pid_label) - box2.add(start_btn) - box2.add(stop_btn) - box1.add(box2) - self.lsp_window.add(box1) - - box1.show_all() - - self.inner_subscribe_to_events() - - def _shutting_down(self): - self.stop_lsp_manager() - - def _tear_down(self, widget, eve): - return True - - def _tggl_lsp_window(self): - if not self.lsp_window.is_visible(): - self.lsp_window.show() - else: - self.lsp_window.hide() - - def subscribe_to_events(self): - self._event_system.subscribe("tggl_lsp_window", self._tggl_lsp_window) - - def inner_subscribe_to_events(self): - self._event_system.subscribe("shutting_down", self._shutting_down) - - self._event_system.subscribe("textDocument/didOpen", self._lsp_did_open) - self._event_system.subscribe("textDocument/didSave", self._lsp_did_save) - self._event_system.subscribe("textDocument/didClose", self._lsp_did_close) - self._event_system.subscribe("textDocument/didChange", self._lsp_did_change) - self._event_system.subscribe("textDocument/definition", self._lsp_goto) - self._event_system.subscribe("textDocument/completion", self._lsp_completion) - - def start_lsp_manager(self, button): - if self.lsp_manager_proc: return - self.lsp_manager_proc = subprocess.Popen(["python", "/opt/lsp-manager.zip"]) - # self.lsp_manager_proc = subprocess.Popen(["lsp-manager"]) - self._load_client_ipc_server() - - def _load_client_ipc_server(self): - self.client_ipc = ClientIPC() - self.client_ipc.set_event_system(self._event_system) - self._ipc_realization_check(self.client_ipc) - - if not self.client_ipc.is_ipc_alive: - raise AppLaunchException(f"LSP IPC Server Already Exists...") - - def _ipc_realization_check(self, ipc_server): - try: - ipc_server.create_ipc_listener() - except Exception: - ipc_server.send_test_ipc_message() - - try: - ipc_server.create_ipc_listener() - except Exception as e: - ... - - def stop_lsp_manager(self, button = None): - if not self.lsp_manager_proc: return - if not self.lsp_manager_proc.poll() is None: - self.lsp_manager_proc = None - return - - self.lsp_manager_proc.terminate() - self.client_ipc.is_ipc_alive = False - self.lsp_manager_proc = None - - def _lsp_did_open(self, language_id: str, uri: str, text: str): - if not self.lsp_manager_proc: return - - data = { - "method": "textDocument/didOpen", - "language_id": language_id, - "uri": uri, - "version": -1, - "text": text, - "line": -1, - "column": -1, - "char": "" - } - - self.send_message(data) - - def _lsp_did_save(self, uri: str, text: str): - if not self.lsp_manager_proc: return - - data = { - "method": "textDocument/didSave", - "language_id": "", - "uri": uri, - "version": -1, - "text": text, - "line": -1, - "column": -1, - "char": "" - } - - self.send_message(data) - - def _lsp_did_close(self, uri: str): - if not self.lsp_manager_proc: return - - data = { - "method": "textDocument/didClose", - "language_id": "", - "uri": uri, - "version": -1, - "text": "", - "line": -1, - "column": -1, - "char": "" - } - - self.send_message(data) - - def _lsp_did_change(self, language_id: str, uri: str, buffer): - if not self.lsp_manager_proc: return - - iter = buffer.get_iter_at_mark( buffer.get_insert() ) - line = iter.get_line() - column = iter.get_line_offset() - start = iter.copy() - end = iter.copy() - - start.backward_line() - start.forward_line() - end.forward_to_line_end() - - text = buffer.get_text(start, end, include_hidden_chars = False) - data = { - "method": "textDocument/didChange", - "language_id": language_id, - "uri": uri, - "version": buffer.version_id, - "text": text, - "line": line, - "column": column, - "char": "" - } - - self.send_message(data) - - def _lsp_goto(self, language_id: str, uri: str, line: int, column: int): - if not self.lsp_manager_proc: return - - data = { - "method": "textDocument/definition", - "language_id": language_id, - "uri": uri, - "version": -1, - "text": "", - "line": line, - "column": column, - "char": "" - } - - self.send_message(data) - - def _lsp_completion(self, source_view): - if not self.lsp_manager_proc: return - - filepath = source_view.get_current_file() - if not filepath: return - - uri = filepath.get_uri() - buffer = source_view.get_buffer() - iter = buffer.get_iter_at_mark( buffer.get_insert() ) - line = iter.get_line() - - char = iter.get_char() - if iter.backward_char(): - char = iter.get_char() - - column = iter.get_line_offset() - data = { - "method": "textDocument/completion", - "language_id": source_view.get_filetype(), - "uri": uri, - "version": source_view.get_version_id(), - "text": "", - "line": line, - "column": column, - "char": char - } - - self.send_message(data) - - def send_message(self, data: dict): - self.client_ipc.send_manager_ipc_message( json.dumps(data) ) \ No newline at end of file