Files
AstrBot/tests/integration/fixtures/echo_lsp_server.py
2026-03-24 17:04:44 +08:00

122 lines
3.6 KiB
Python

"""
Echo LSP Server - Simple LSP server for testing.
This server responds to LSP protocol requests over stdio with Content-Length headers:
- initialize: Initialize the connection and report capabilities
- initialized: Notification (no response)
- shutdown: Graceful shutdown request
- textDocument/didOpen: Document open notification
- other requests: Echoed back with result
"""
from __future__ import annotations
import json
import sys
def write_message(message: dict) -> None:
"""Write a JSON-RPC message to stdout with Content-Length header."""
content = json.dumps(message)
headers = f"Content-Length: {len(content)}\r\n\r\n"
sys.stdout.write(headers + content)
sys.stdout.flush()
def read_message() -> dict | None:
"""Read a single JSON-RPC message from stdin using LSP stdio protocol."""
# Read headers until we get \r\n\r\n
header_bytes = b""
while True:
ch = sys.stdin.buffer.read(1)
if not ch:
return None
header_bytes += ch
if header_bytes.endswith(b"\r\n\r\n"):
break
# Parse Content-Length from headers
header_text = header_bytes.decode("utf-8")
content_length = 0
for line in header_text.split("\r\n"):
if line.startswith("Content-Length:"):
content_length = int(line.split(":")[1].strip())
if content_length == 0:
return None
# Read the content body
content = sys.stdin.buffer.read(content_length)
if not content:
return None
return json.loads(content.decode("utf-8"))
def main() -> None:
"""Main loop - read requests from stdin and respond."""
while True:
try:
request = read_message()
if request is None:
break
method = request.get("method", "")
req_id = request.get("id")
# Handle initialize request
if method == "initialize":
response = {
"jsonrpc": "2.0",
"id": req_id,
"result": {
"capabilities": {
"textDocumentSync": 1,
"hoverProvider": True,
"completionProvider": {"resolveProvider": False},
}
},
"serverInfo": {
"name": "echo-lsp-server",
"version": "1.0.0",
},
}
write_message(response)
# Handle initialized notification (no response needed)
elif method == "initialized":
continue
# Handle shutdown request
elif method == "shutdown":
response = {
"jsonrpc": "2.0",
"id": req_id,
"result": None,
}
write_message(response)
# Handle textDocument/didOpen notification (no response needed)
elif method == "textDocument/didOpen":
continue
# Echo any other request back with the same id
elif req_id is not None:
response = {
"jsonrpc": "2.0",
"id": req_id,
"result": {"echoed": True, "method": method},
}
write_message(response)
except Exception as e:
error_response = {
"jsonrpc": "2.0",
"id": None,
"error": {"code": -32603, "message": str(e)},
}
write_message(error_response)
if __name__ == "__main__":
main()