This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-9p/tools/_lib/lsp/client.py

78 lines
2.1 KiB
Python

"""Client library for LSP tool scripts. Connects to the bridge daemon, starting it if needed."""
import json
import os
import socket
import subprocess
import sys
import time
SOCK_PATH = os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "ollie-lsp.sock")
PID_PATH = SOCK_PATH + ".pid"
def _bridge_running() -> bool:
if not os.path.exists(PID_PATH):
return False
try:
pid = int(open(PID_PATH).read().strip())
os.kill(pid, 0)
return True
except (OSError, ValueError):
return False
def _start_bridge():
"""Start the bridge daemon as a background process."""
lib_dir = os.path.dirname(os.path.abspath(__file__))
bridge_script = os.path.join(lib_dir, "bridge.py")
subprocess.Popen(
[sys.executable, bridge_script],
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True,
)
# Wait for socket to appear
for _ in range(50):
if os.path.exists(SOCK_PATH):
return
time.sleep(0.1)
print("error: bridge daemon failed to start", file=sys.stderr)
sys.exit(1)
def request(method: str, **kwargs) -> dict:
"""Send a request to the bridge and return the response."""
if not _bridge_running():
_start_bridge()
payload = {"method": method, **kwargs}
data = json.dumps(payload).encode()
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect(SOCK_PATH)
sock.sendall(data)
sock.shutdown(socket.SHUT_WR)
chunks = []
while True:
chunk = sock.recv(4096)
if not chunk:
break
chunks.append(chunk)
response = json.loads(b"".join(chunks))
except ConnectionRefusedError:
# Stale socket, restart
if os.path.exists(SOCK_PATH):
os.unlink(SOCK_PATH)
_start_bridge()
return request(method, **kwargs)
finally:
sock.close()
if "error" in response:
print(f"error: {response['error']}", file=sys.stderr)
sys.exit(1)
return response.get("result")