Skip to content

Commit 813a10e

Browse files
committed
Simplify the Python files used to write BSP servers for SourceKit-LSP tests
1 parent d8b41d4 commit 813a10e

File tree

12 files changed

+164
-580
lines changed

12 files changed

+164
-580
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import json
2+
import sys
3+
from typing import Optional
4+
5+
6+
class RequestError(Exception):
7+
"""
8+
An error that can be thrown from a request handling function in `AbstractBuildServer` to return an error response to
9+
SourceKit-LSP.
10+
"""
11+
12+
code: int
13+
message: str
14+
15+
def __init__(self, code: int, message: str):
16+
self.code = code
17+
self.message = message
18+
19+
20+
class AbstractBuildServer:
21+
"""
22+
An abstract class to implement a BSP server in Python for SourceKit-LSP testing purposes.
23+
"""
24+
25+
def run(self):
26+
"""
27+
Run the build server. This should be called from the top-level code of the build server's Python file.
28+
"""
29+
while True:
30+
line = sys.stdin.readline()
31+
if len(line) == 0:
32+
break
33+
34+
assert line.startswith("Content-Length:")
35+
length = int(line[len("Content-Length:") :])
36+
sys.stdin.readline()
37+
message = json.loads(sys.stdin.read(length))
38+
39+
try:
40+
result = self.handle_message(message)
41+
if result:
42+
response_message: dict[str, object] = {
43+
"jsonrpc": "2.0",
44+
"id": message["id"],
45+
"result": result,
46+
}
47+
self.send_raw_message(response_message)
48+
except RequestError as e:
49+
error_response_message: dict[str, object] = {
50+
"jsonrpc": "2.0",
51+
"id": message["id"],
52+
"error": {
53+
"code": e.code,
54+
"message": e.message,
55+
},
56+
}
57+
self.send_raw_message(error_response_message)
58+
59+
def handle_message(self, message: dict[str, object]) -> Optional[dict[str, object]]:
60+
"""
61+
Dispatch handling of the given method, received from SourceKit-LSP to the message handling function.
62+
"""
63+
method: str = str(message["method"])
64+
params: dict[str, object] = message["params"] # type: ignore
65+
if method == "build/exit":
66+
return self.exit(params)
67+
elif method == "build/initialize":
68+
return self.initialize(params)
69+
elif method == "build/initialized":
70+
return self.initialized(params)
71+
elif method == "build/shutdown":
72+
return self.shutdown(params)
73+
elif method == "textDocument/registerForChanges":
74+
return self.register_for_changes(params)
75+
76+
# ignore other notifications
77+
if "id" in message:
78+
raise RequestError(code=-32601, message=f"Method not found: {method}")
79+
80+
def send_raw_message(self, message: dict[str, object]):
81+
"""
82+
Send a raw message to SourceKit-LSP. The message needs to have all JSON-RPC wrapper fields.
83+
84+
Subclasses should not call this directly
85+
"""
86+
message_str = json.dumps(message)
87+
sys.stdout.buffer.write(
88+
f"Content-Length: {len(message_str)}\r\n\r\n{message_str}".encode("utf-8")
89+
)
90+
sys.stdout.flush()
91+
92+
def send_notification(self, method: str, params: dict[str, object]):
93+
"""
94+
Send a notification with the given method and parameters to SourceKit-LSP.
95+
"""
96+
message: dict[str, object] = {
97+
"jsonrpc": "2.0",
98+
"method": method,
99+
"params": params,
100+
}
101+
self.send_raw_message(message)
102+
103+
# Message handling functions.
104+
# Subclasses should override these to provide functionality.
105+
106+
def exit(self, notification: dict[str, object]) -> None:
107+
pass
108+
109+
def initialize(self, request: dict[str, object]) -> dict[str, object]:
110+
return {
111+
"displayName": "test server",
112+
"version": "0.1",
113+
"bspVersion": "2.0",
114+
"rootUri": "blah",
115+
"capabilities": {"languageIds": ["a", "b"]},
116+
"data": {
117+
"indexDatabasePath": "some/index/db/path",
118+
"indexStorePath": "some/index/store/path",
119+
},
120+
}
121+
122+
def initialized(self, notification: dict[str, object]) -> None:
123+
pass
124+
125+
def register_for_changes(self, notification: dict[str, object]):
126+
pass
127+
128+
def shutdown(self, notification: dict[str, object]) -> None:
129+
pass

Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/buildServer.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py

Lines changed: 0 additions & 75 deletions
This file was deleted.

Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/buildServer.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/server.py

Lines changed: 0 additions & 98 deletions
This file was deleted.

Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargets/buildServer.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)