Skip to content

Commit eac7700

Browse files
weckrMarek Budikrchl
authored
support client config with tcp_port but without command (#2300)
Co-authored-by: Marek Budik <[email protected]> Co-authored-by: Rafal Chlodnicki <[email protected]>
1 parent 69f383d commit eac7700

File tree

3 files changed

+37
-30
lines changed

3 files changed

+37
-30
lines changed

docs/src/client_configuration.md

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ The vast majority of language servers can communicate over stdio. To use stdio,
6565

6666
Some language servers can also act as a TCP server accepting incoming TCP connections. So: the language server subprocess is started by this package, and the subprocess will then open a TCP listener port. The editor can then connect as a client and initiate the communication. To use this mode, set `tcp_port` to a positive number designating the port to connect to on `localhost`.
6767

68+
Optionally in this case, you can omit the `command` setting if you don't want Sublime LSP to manage the language server process and you'll take care of it yourself.
69+
6870
### TCP - localhost - editor acts as a TCP server
6971

7072
Some _LSP servers_ instead expect the _LSP client_ to act as a _TCP server_. The _LSP server_ will then connect as a _TCP client_, after which the _LSP client_ is expected to initiate the communication. To use this mode, set `tcp_port` to a negative number designating the port to bind to for accepting new TCP connections.

docs/src/language_servers.md

+2
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ Follow installation instructions on [LSP-gopls](https://github.com/sublimelsp/LS
188188
}
189189
```
190190
191+
If you encounter high cpu load or any other issues you can try omitting the [command] line, and ensure the godot editor is running while you work in sublime.
192+
191193
## GraphQL
192194
193195
Follow installation instructions on [LSP-graphql](https://github.com/sublimelsp/LSP-graphql).

plugin/core/transports.py

+33-30
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ def _decode(message: bytes) -> Dict[str, Any]:
9494

9595
class ProcessTransport(Transport[T]):
9696

97-
def __init__(self, name: str, process: subprocess.Popen, socket: Optional[socket.socket], reader: IO[bytes],
98-
writer: IO[bytes], stderr: Optional[IO[bytes]], processor: AbstractProcessor[T],
99-
callback_object: TransportCallbacks[T]) -> None:
97+
def __init__(self, name: str, process: Optional[subprocess.Popen], socket: Optional[socket.socket],
98+
reader: IO[bytes], writer: IO[bytes], stderr: Optional[IO[bytes]],
99+
processor: AbstractProcessor[T], callback_object: TransportCallbacks[T]) -> None:
100100
self._closed = False
101101
self._process = process
102102
self._socket = socket
@@ -106,12 +106,13 @@ def __init__(self, name: str, process: subprocess.Popen, socket: Optional[socket
106106
self._processor = processor
107107
self._reader_thread = threading.Thread(target=self._read_loop, name='{}-reader'.format(name))
108108
self._writer_thread = threading.Thread(target=self._write_loop, name='{}-writer'.format(name))
109-
self._stderr_thread = threading.Thread(target=self._stderr_loop, name='{}-stderr'.format(name))
110109
self._callback_object = weakref.ref(callback_object)
111110
self._send_queue = Queue(0) # type: Queue[Union[T, None]]
112111
self._reader_thread.start()
113112
self._writer_thread.start()
114-
self._stderr_thread.start()
113+
if stderr:
114+
self._stderr_thread = threading.Thread(target=self._stderr_loop, name='{}-stderr'.format(name))
115+
self._stderr_thread.start()
115116

116117
def send(self, payload: T) -> None:
117118
self._send_queue.put_nowait(payload)
@@ -135,7 +136,8 @@ def __del__(self) -> None:
135136
self.close()
136137
self._join_thread(self._writer_thread)
137138
self._join_thread(self._reader_thread)
138-
self._join_thread(self._stderr_thread)
139+
if self._stderr_thread:
140+
self._join_thread(self._stderr_thread)
139141

140142
def _read_loop(self) -> None:
141143
exception = None
@@ -164,23 +166,24 @@ def invoke(p: T) -> None:
164166

165167
def _end(self, exception: Optional[Exception]) -> None:
166168
exit_code = 0
167-
if not exception:
168-
try:
169-
# Allow the process to stop itself.
170-
exit_code = self._process.wait(1)
171-
except (AttributeError, ProcessLookupError, subprocess.TimeoutExpired):
172-
pass
173-
if self._process.poll() is None:
174-
try:
175-
# The process didn't stop itself. Terminate!
176-
self._process.kill()
177-
# still wait for the process to die, or zombie processes might be the result
178-
# Ignore the exit code in this case, it's going to be something non-zero because we sent SIGKILL.
179-
self._process.wait()
180-
except (AttributeError, ProcessLookupError):
181-
pass
182-
except Exception as ex:
183-
exception = ex # TODO: Old captured exception is overwritten
169+
if self._process:
170+
if not exception:
171+
try:
172+
# Allow the process to stop itself.
173+
exit_code = self._process.wait(1)
174+
except (AttributeError, ProcessLookupError, subprocess.TimeoutExpired):
175+
pass
176+
if self._process.poll() is None:
177+
try:
178+
# The process didn't stop itself. Terminate!
179+
self._process.kill()
180+
# still wait for the process to die, or zombie processes might be the result
181+
# Ignore the exit code in this case, it's going to be something non-zero because we sent SIGKILL.
182+
self._process.wait()
183+
except (AttributeError, ProcessLookupError):
184+
pass
185+
except Exception as ex:
186+
exception = ex # TODO: Old captured exception is overwritten
184187

185188
def invoke() -> None:
186189
callback_object = self._callback_object()
@@ -252,13 +255,12 @@ def start_subprocess() -> subprocess.Popen:
252255
if config.listener_socket:
253256
assert isinstance(config.tcp_port, int) and config.tcp_port > 0
254257
process, sock, reader, writer = _await_tcp_connection(
255-
config.name,
256-
config.tcp_port,
257-
config.listener_socket,
258-
start_subprocess
259-
)
258+
config.name, config.tcp_port, config.listener_socket, start_subprocess)
260259
else:
261-
process = start_subprocess()
260+
if config.command:
261+
process = start_subprocess()
262+
elif not config.tcp_port:
263+
raise RuntimeError("Failed to provide command or tcp_port, at least one of them has to be configured")
262264
if config.tcp_port:
263265
sock = _connect_tcp(config.tcp_port)
264266
if sock is None:
@@ -270,8 +272,9 @@ def start_subprocess() -> subprocess.Popen:
270272
writer = process.stdin # type: ignore
271273
if not reader or not writer:
272274
raise RuntimeError('Failed initializing transport: reader: {}, writer: {}'.format(reader, writer))
275+
stderr = process.stderr if process else None
273276
return ProcessTransport(
274-
config.name, process, sock, reader, writer, process.stderr, json_rpc_processor, callback_object) # type: ignore
277+
config.name, process, sock, reader, writer, stderr, json_rpc_processor, callback_object) # type: ignore
275278

276279

277280
_subprocesses = weakref.WeakSet() # type: weakref.WeakSet[subprocess.Popen]

0 commit comments

Comments
 (0)