115 lines
3.4 KiB
Python
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()
|
|
|