115 lines
3.4 KiB
Python

import os
import sys
import inspect
import json
from subprocess import run, Popen, PIPE
from threading import Thread
from xdg import xdg_data_home
CONTENT_LENGTH = "Content-Length: "
def process_messages (source, sink, handlers, log):
print("start message handler", file=log)
while True:
line = source.readline()
if not line:
break
line = line.decode()
if line.startswith(CONTENT_LENGTH):
content_length = int(line[len(CONTENT_LENGTH):].strip())
print(f">> ce: [{content_length}]", file=log)
source.readline()
payload = source.read(content_length).decode()
print(f">> payload: [{payload}]", file=log)
if handlers:
payload_parsed = json.loads(payload)
for handler in handlers:
try:
handler(payload_parsed)
except Exception as e:
print(f"Error from handler: {e}", file=log)
transmit_payload(payload, sink)
print("stop message handler", file=log)
def transmit_payload (payload, sink):
if isinstance(payload, dict):
payload = json.dumps(payload)
sink.write(f"{CONTENT_LENGTH}{len(payload)}\r\n\r\n{payload}".encode())
sink.flush()
class Handlers:
def __init__ (self):
self.handlers = []
def __call__ (self, func):
self.handlers.append(func)
return func
def get_handlers (self, server):
return [lambda payload: handler(server, payload) for handler in self.handlers]
handler = Handlers()
def method (name):
def method_handler (func):
def handle_method (server, payload):
if payload.get("method", None) != name:
return
func(server, payload.get("params"))
return handler(handle_method)
return method_handler
def command (name):
def command_handler (func):
def handle_command (server, payload):
if payload.get("command", None) != name:
return
func(server, payload.get("arguments"))
return method("workspace/executeCommand")(handle_command)
return command_handler
def get_data_home ():
data_directory = os.path.join(xdg_data_home(), "lsp-proxy")
if not os.path.exists(data_directory):
os.makedirs(data_directory)
return data_directory
class LspServer:
def __init__ (self, command):
self.command = command
self.handlers = []
def start (self):
handlers = self.handlers + handler.get_handlers(self)
with Popen(self.command, stdin=PIPE, stdout=PIPE) as process:
self.process = process
self.process_reader = Thread(target=process_messages, args=[process.stdout, sys.stdout.buffer, handlers, sys.stderr])
self.process_reader.start()
process_messages(sys.stdin.buffer, process.stdin, handlers, sys.stderr)
def start2 (self):
run(self.command)
def send_to_client (self, payload):
transmit_payload(payload, sys.stdout.buffer)
def send_to_server (self, payload):
transmit_payload(payload, self.process.stdout)
def close (self):
if self.process:
self.process.terminate()