message refactoring; initial event call from client structure

This commit is contained in:
itdominator 2024-09-09 21:50:52 -05:00
parent 6df84e9f23
commit 13b126ef6e
8 changed files with 206 additions and 114 deletions

View File

@ -1,7 +1,6 @@
# Python imports # Python imports
import os import os
import signal import signal
import json
import subprocess import subprocess
import threading import threading
@ -9,12 +8,17 @@ import threading
from gi.repository import GLib from gi.repository import GLib
# Application imports # Application imports
from libs.dto.lsp_message_structs import MessageEncoder, LSPRequest, LSPNotification, LSPResponse from libs.dto.lsp_messages import LEN_HEADER, TYPE_HEADER, get_message_str, get_message_obj, definition_query, references_query, symbols_query
from libs.dto.lsp_message_structs import ClientRequest, ClientNotification, LSPResponse
LEN_HEADER = "Content-Length: " def _log_list():
TYPE_HEADER = "Content-Type: " def add_log_entry(title: str, message: any):
...
def clear():
...
@ -22,6 +26,8 @@ class LSPController:
def __init__(self): def __init__(self):
super(LSPController).__init__() super(LSPController).__init__()
self._language = "python3"
# https://github.com/microsoft/multilspy/tree/main/src/multilspy/language_servers # https://github.com/microsoft/multilspy/tree/main/src/multilspy/language_servers
# initialize-params-slim.json was created off of jedi_language_server one # initialize-params-slim.json was created off of jedi_language_server one
self._init_params = settings_manager.get_lsp_init_data() self._init_params = settings_manager.get_lsp_init_data()
@ -32,12 +38,44 @@ class LSPController:
self.read_lock = threading.Lock() self.read_lock = threading.Lock()
self.write_lock = threading.Lock() self.write_lock = threading.Lock()
self.log_list = _log_list
self.request_list = {}
self._subscribe_to_events()
def _subscribe_to_events(self):
event_system.subscribe("client-send-request", self._client_send_request)
event_system.subscribe("client-send-notification", self._client_send_notification)
def _client_send_request(self, method: str, uri: str, line: int, character: int):
if not method: return
if "textDocument/definition":
params = symbols_query
elif "textDocument/references":
params = symbols_query
elif "textDocument/documentSymbol":
params = symbols_query
params["textDocument"]["uri"] = uri
params["textDocument"]["languageId"] = self._language
params["position"]["line"] = line
params["position"]["character"] = character
self.send_request(method, params)
def _client_send_notification(self, method: str, line: int, char: int):
if not method: return
self.send_notification(method, params)
def set_language(self, language):
self._language = language
def set_log_list(self, log_list): def set_log_list(self, log_list):
self.log_list = log_list self.log_list = log_list
def set_start_command(self, start_command: []): def set_start_command(self, start_command: list):
self._start_command = start_command self._start_command = start_command
def unset_start_command(self): def unset_start_command(self):
@ -56,25 +94,26 @@ class LSPController:
} }
] ]
self._init_params["initializationOptions"] = json.loads(init_ops) self._init_params["initializationOptions"] = get_message_obj(init_ops)
self.send_request("initialize", self._init_params) self.send_request("initialize", self._init_params)
def send_initialized_message(self): def send_initialized_message(self):
self.send_notification("initialized") self.send_notification("initialized")
def send_notification(self, method: str, params: {} = {}): def send_notification(self, method: str, params: {} = {}):
self._send_message( LSPNotification(method, params) ) self._send_message( ClientNotification(method, params) )
def send_request(self, method: str, params: {} = {}): def send_request(self, method: str, params: {} = {}):
self._monitor_lsp_response() self._monitor_lsp_response()
self._send_message( LSPRequest(self._message_id, method, params) ) self._send_message( ClientRequest(self._message_id, method, params) )
self.request_list[self._message_id] = {"method": method}
self._message_id += 1 self._message_id += 1
def _send_message(self, data: LSPRequest or LSPNotification): def _send_message(self, data: ClientRequest or ClientNotification):
if not data or not hasattr(self, "lsp_process"): return if not data or not hasattr(self, "lsp_process"): return
message_str = json.dumps(data, cls = MessageEncoder) message_str = get_message_str(data)
message_size = len(message_str) message_size = len(message_str)
message = f"Content-Length: {message_size}\r\n\r\n{message_str}" message = f"Content-Length: {message_size}\r\n\r\n{message_str}"
@ -170,33 +209,36 @@ class LSPController:
data = self.lsp_process.stdout.read(message_size) data = self.lsp_process.stdout.read(message_size)
jsonrpc_res = data.decode("utf-8") jsonrpc_res = data.decode("utf-8")
lsp_response = json.loads( jsonrpc_res ) lsp_response = LSPResponse(**get_message_obj(jsonrpc_res))
response_id = -1 response_id = -1
if not lsp_response: return if not lsp_response: return
if "id" in lsp_response.keys(): response_id = lsp_response["id"] GLib.idle_add(self.handle_lsp_response, lsp_response)
GLib.idle_add(self.handle_lsp_response, LSPResponse(response_id, lsp_response))
def handle_lsp_response(self, lsp_response: LSPResponse): def handle_lsp_response(self, lsp_response: LSPResponse):
self.log_list.add_log_entry("LSP Response", lsp_response) method = self.request_list[lsp_response.id]
result = lsp_response.result result = lsp_response.result
keys = result.keys()
if "error" in keys: self.log_list.add_log_entry("LSP Response", lsp_response)
error = result["error"] self.process_lsp_response(method, result)
logger.debug(f"LSP Error Code: {error['code']}")
logger.debug(f"LSP Error Message:\n{error['message']}")
return
if "result" in keys:
result = result["result"]
def process_lsp_response(self, method, result):
if isinstance(result, dict): if isinstance(result, dict):
keys = result.keys() keys = result.keys()
if "capabilities" in keys: return
... if "error" in keys:
error = result["error"]
logger.debug(f"LSP Error Code: {error['code']}")
logger.debug(f"LSP Error Message:\n{error['message']}")
return
if "result" in keys:
result = result["result"]
if isinstance(result, dict):
keys = result.keys()
if "capabilities" in keys:
...
if isinstance(result, list): if isinstance(result, list):
... ...

View File

@ -6,64 +6,7 @@ gi.require_version('GtkSource', '4')
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports # Application imports
from libs.dto.lsp_messages import get_message_str, content_part
# Request type formatting
# https://github.com/microsoft/multilspy/blob/main/src/multilspy/language_server.py#L417
content_part = """{
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
},
"position": {
"line": 5,
"character": 12,
"offset": 0
}
}
}
"""
references_query = """{
"method": "textDocument/references",
"params": {
"context": {
"includeDeclaration": false
},
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
},
"position": {
"line": 30,
"character": 13,
"offset": 0
}
}
}
"""
symbols_query = """{
"method": "textDocument/documentSymbol",
"params": {
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
}
}
}
"""
@ -97,7 +40,7 @@ class LspMessageSourceView(GtkSource.View):
buffer.set_language( language_manager.get_language("json") ) buffer.set_language( language_manager.get_language("json") )
buffer.set_style_scheme(style_scheme) buffer.set_style_scheme(style_scheme)
buffer.set_text(content_part) buffer.set_text( get_message_str(content_part) )
def _setup_signals(self): def _setup_signals(self):

View File

@ -1,5 +1,4 @@
# Python imports # Python imports
import json
# Lib imports # Lib imports
import gi import gi
@ -8,7 +7,9 @@ from gi.repository import Gtk
from gi.repository import GLib from gi.repository import GLib
# Application imports # Application imports
from libs.dto.lsp_message_structs import MessageEncoder, LSPRequest, LSPNotification, LSPResponse from libs.dto.lsp_messages import get_message_str
from libs.dto.lsp_message_structs import MessageTypes
class LogList(Gtk.ListBox): class LogList(Gtk.ListBox):
@ -28,13 +29,13 @@ class LogList(Gtk.ListBox):
ctx.add_class("log-list-listbox") ctx.add_class("log-list-listbox")
self.set_placeholder( Gtk.Label(label="Empty Log List...") ) self.set_placeholder( Gtk.Label(label="Empty Log List...") )
self.set_selection_mode(Gtk.SelectionMode.NONE) self.set_selection_mode( Gtk.SelectionMode.NONE )
def _setup_signals(self): def _setup_signals(self):
def tggl_row_view(lb, row): def tggl_row_view(lb, row):
frame = row.get_child() frame = row.get_child()
revealer = frame.get_children()[0] revealer = frame.get_children()[0]
revealer.set_reveal_child(not revealer.get_reveal_child()) revealer.set_reveal_child( not revealer.get_reveal_child() )
del frame del frame
del revealer del revealer
@ -51,12 +52,12 @@ class LogList(Gtk.ListBox):
def _load_widgets(self): def _load_widgets(self):
... ...
def add_log_entry(self, m_type: str, data: LSPRequest or LSPNotification or LSPResponse): def add_log_entry(self, m_type: str, data: MessageTypes):
message = json.dumps(data, separators = (',', ':'), indent = 4, cls = MessageEncoder) message = get_message_str(data)
frame = Gtk.Frame() frame = Gtk.Frame()
revealer = Gtk.Revealer() revealer = Gtk.Revealer()
label = Gtk.Label(label=message) label = Gtk.Label(label = message)
lbl_str = f"> {m_type} Message: {data.id}" if hasattr(data, "id") else f"> {m_type} Notification" lbl_str = f"> {m_type} Message: {data.id}" if hasattr(data, "id") else f"> {m_type} Notification"
label.set_xalign(0.010) label.set_xalign(0.010)

View File

@ -1,5 +1,4 @@
# Python imports # Python imports
import json
# Lib imports # Lib imports
import gi import gi
@ -7,6 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from libs.dto.lsp_messages import get_message_obj
from core.controllers.lsp_controller import LSPController from core.controllers.lsp_controller import LSPController
from .buttons.top_button_box import TopButtonBox from .buttons.top_button_box import TopButtonBox
from .enteries.lsp_message_source_view import LspMessageSourceView from .enteries.lsp_message_source_view import LspMessageSourceView
@ -15,12 +15,9 @@ from .buttons.bottom_button_box import BottomButtonBox
class LSPMessageBox(Gtk.Box): class LSPMessageBox(Gtk.Box):
def __init__(self, log_list): def __init__(self):
super(LSPMessageBox, self).__init__() super(LSPMessageBox, self).__init__()
self.lsp_controller = LSPController()
self.lsp_controller.set_log_list(log_list)
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
@ -56,6 +53,12 @@ class LSPMessageBox(Gtk.Box):
self.add(scrolled_win) self.add(scrolled_win)
self.add(bottom_buttons) self.add(bottom_buttons)
def create_lsp_controller(self, language, log_list):
self.lsp_controller = LSPController()
self.lsp_controller.set_language(language)
self.lsp_controller.set_log_list(log_list)
def update_message_id_label(self): def update_message_id_label(self):
self.top_buttons.update_message_id_lbl( self.top_buttons.update_message_id_lbl(
self.lsp_controller.get_message_id() self.lsp_controller.get_message_id()
@ -74,11 +77,11 @@ class LSPMessageBox(Gtk.Box):
self.lsp_controller.send_initialized_message() self.lsp_controller.send_initialized_message()
def button_send_notification(self): def button_send_notification(self):
message = json.loads( self.lsp_msg_src_vw.get_text_str() ) message = get_message_obj( self.lsp_msg_src_vw.get_text_str() )
self.lsp_controller.send_notification(message["method"], message["params"]) self.lsp_controller.send_notification(message["method"], message["params"])
def button_send_request(self): def button_send_request(self):
message = json.loads( self.lsp_msg_src_vw.get_text_str() ) message = get_message_obj( self.lsp_msg_src_vw.get_text_str() )
self.lsp_controller.send_request(message["method"], message["params"]) self.lsp_controller.send_request(message["method"], message["params"])
self.update_message_id_label() self.update_message_id_label()

View File

@ -65,7 +65,7 @@ class LSPUI(Gtk.Grid):
self.command_entry = CommandEntry( self._data["command"] ) self.command_entry = CommandEntry( self._data["command"] )
self.socket_entry = SocketEntry( self._data["socket"] ) self.socket_entry = SocketEntry( self._data["socket"] )
self.init_ops_src_vw = InitOptionsSourceView( self._data["initialization-options"] ) self.init_ops_src_vw = InitOptionsSourceView( self._data["initialization-options"] )
self.message_box = LSPMessageBox(self.log_list) message_box = LSPMessageBox()
init_options_lbl.set_margin_top(10) init_options_lbl.set_margin_top(10)
message_box_lbl.set_margin_top(10) message_box_lbl.set_margin_top(10)
@ -76,6 +76,8 @@ class LSPUI(Gtk.Grid):
scrolled_win1.add(self.init_ops_src_vw) scrolled_win1.add(self.init_ops_src_vw)
scrolled_win2.add(self.log_list) scrolled_win2.add(self.log_list)
message_box.create_lsp_controller(self._language, self.log_list)
# child, left, top, width, height # child, left, top, width, height
self.attach(self.link_btn, 0, 0, 3, 1) self.attach(self.link_btn, 0, 0, 3, 1)
@ -91,7 +93,7 @@ class LSPUI(Gtk.Grid):
self.attach(scrolled_win1, 0, 5, 3, 1) self.attach(scrolled_win1, 0, 5, 3, 1)
self.attach(message_box_lbl, 0, 6, 3, 1) self.attach(message_box_lbl, 0, 6, 3, 1)
self.attach(self.message_box, 0, 7, 3, 1) self.attach(message_box, 0, 7, 3, 1)
self.attach(log_list_lbl, 3, 0, 3, 1) self.attach(log_list_lbl, 3, 0, 3, 1)
self.attach(scrolled_win2, 3, 1, 3, 11) self.attach(scrolled_win2, 3, 1, 3, 11)

View File

@ -1,6 +1,7 @@
# Python imports # Python imports
import json import json
import enum import enum
from dataclasses import dataclass
# Lib imports # Lib imports
@ -18,11 +19,11 @@ class MessageEncoder(json.JSONEncoder):
return o.__dict__ return o.__dict__
@dataclass
class LSPRequest(object): class ClientRequest(object):
def __init__(self, id: int, method: str, params: dict): def __init__(self, id: int, method: str, params: dict):
""" """
Constructs a new LSP Request instance. Constructs a new Client Request instance.
:param int id: Message id to track instance. :param int id: Message id to track instance.
:param str method: The type of lsp request being made. :param str method: The type of lsp request being made.
@ -33,11 +34,11 @@ class LSPRequest(object):
self.method = method self.method = method
self.params = params self.params = params
@dataclass
class LSPNotification(object): class ClientNotification(object):
def __init__(self, method: str, params: dict): def __init__(self, method: str, params: dict):
""" """
Constructs a new LSP Notification instance. Constructs a new Client Notification instance.
:param str method: The type of lsp notification being made. :param str method: The type of lsp notification being made.
:param dict params: The arguments of the given method. :param dict params: The arguments of the given method.
@ -46,15 +47,18 @@ class LSPNotification(object):
self.method = method self.method = method
self.params = params self.params = params
@dataclass
class LSPResponse(object): class LSPResponse(object):
def __init__(self, id: int or None, result: dict):
""" """
Constructs a new LSP Response instance. Constructs a new LSP Response instance.
:param str method: The type of lsp notification being made. :param str method: The type of lsp notification being made.
:param dict params: The arguments of the given method. :param dict result: The arguments of the given method.
""" """
self.jsonrpc = "2.0" jsonrpc: str
self.id = id id: int or None
self.result = result result: {}
class MessageTypes(ClientRequest, ClientNotification, LSPResponse):
...

View File

@ -0,0 +1,92 @@
# Python imports
import json
# Lib imports
# Application imports
from .lsp_message_structs import MessageEncoder
LEN_HEADER = "Content-Length: "
TYPE_HEADER = "Content-Type: "
def get_message_str(data: dict) -> str:
return json.dumps(data, separators = (',', ':'), indent = 4, cls = MessageEncoder)
def get_message_obj(data: str):
return json.loads(data)
# Request type formatting
# https://github.com/microsoft/multilspy/blob/main/src/multilspy/language_server.py#L417
content_part = {
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///",
"languageId": "python3",
"version": 1,
"text": ""
},
"position": {
"line": 5,
"character": 12,
"offset": 0
}
}
}
definition_query = {
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
},
"position": {
"line": 5,
"character": 12,
"offset": 0
}
}
}
references_query = {
"method": "textDocument/references",
"params": {
"context": {
"includeDeclaration": False
},
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
},
"position": {
"line": 30,
"character": 13,
"offset": 0
}
}
}
symbols_query = {
"method": "textDocument/documentSymbol",
"params": {
"textDocument": {
"uri": "file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/LSP-Manager/src/core/widgets/lsp_message_box.py",
"languageId": "python3",
"version": 1,
"text": ""
}
}
}

View File

@ -1,5 +1,10 @@
# Python imports
import enum import enum
# Lib imports
# Application imports
def to_type(o, new_type): def to_type(o, new_type):