1
1
""" A session for managing a language server process
2
2
"""
3
3
import atexit
4
+ import math
4
5
import os
5
6
import string
6
7
import subprocess
14
15
import anyio
15
16
from anyio import CancelScope
16
17
from anyio .abc import Process , SocketStream
18
+ from anyio .streams .stapled import StapledObjectStream
17
19
from tornado .ioloop import IOLoop
18
- from tornado .queues import Queue
19
20
from tornado .websocket import WebSocketHandler
20
21
from traitlets import Bunch , Float , Instance , Set , Unicode , UseEnum , observe
21
22
from traitlets .config import LoggingConfigurable
@@ -62,10 +63,14 @@ class LanguageServerSessionBase(
62
63
writer = Instance (LspStreamWriter , help = "the JSON-RPC writer" , allow_none = True )
63
64
reader = Instance (LspStreamReader , help = "the JSON-RPC reader" , allow_none = True )
64
65
from_lsp = Instance (
65
- Queue , help = "a queue for string messages from the server" , allow_none = True
66
+ StapledObjectStream ,
67
+ help = "a queue for string messages from the server" ,
68
+ allow_none = True
66
69
)
67
70
to_lsp = Instance (
68
- Queue , help = "a queue for string message to the server" , allow_none = True
71
+ StapledObjectStream ,
72
+ help = "a queue for string messages to the server" ,
73
+ allow_none = True
69
74
)
70
75
handlers = Set (
71
76
trait = Instance (WebSocketHandler ),
@@ -80,6 +85,10 @@ class LanguageServerSessionBase(
80
85
5 ,
81
86
help = "timeout after which a process will be terminated forcefully" ,
82
87
).tag (config = True )
88
+ queue_size = Float (
89
+ math .inf ,
90
+ help = "the maximum number of messages that can be buffered in the queue"
91
+ ).tag (config = True )
83
92
84
93
_skip_serialize = ["argv" , "debug_argv" ]
85
94
@@ -170,6 +179,12 @@ async def cleanup(self):
170
179
if self .process is not None :
171
180
await self .stop_process (self .stop_timeout )
172
181
self .process = None
182
+ if self .from_lsp is not None :
183
+ await self .from_lsp .aclose ()
184
+ self .from_lsp = None
185
+ if self .to_lsp is not None :
186
+ await self .to_lsp .aclose ()
187
+ self .to_lsp = None
173
188
174
189
self .status = SessionStatus .STOPPED
175
190
@@ -184,7 +199,7 @@ def _on_handlers(self, change: Bunch):
184
199
def write (self , message ):
185
200
"""wrapper around the write queue to keep it mostly internal"""
186
201
self .last_handler_message_at = self .now ()
187
- self .thread_loop .add_callback (self .to_lsp .put_nowait , message )
202
+ self .thread_loop .add_callback (self .to_lsp .send , message )
188
203
189
204
def now (self ):
190
205
return datetime .now (timezone .utc )
@@ -242,8 +257,10 @@ async def stop_process(self, timeout: int = 5):
242
257
243
258
def init_queues (self ):
244
259
"""create the queues"""
245
- self .from_lsp = Queue ()
246
- self .to_lsp = Queue ()
260
+ self .from_lsp = StapledObjectStream (
261
+ * anyio .create_memory_object_stream (max_buffer_size = self .queue_size ))
262
+ self .to_lsp = StapledObjectStream (
263
+ * anyio .create_memory_object_stream (max_buffer_size = self .queue_size ))
247
264
248
265
def substitute_env (self , env , base ):
249
266
final_env = copy (os .environ )
@@ -286,7 +303,6 @@ async def _broadcast_from_lsp(self):
286
303
self .last_server_message_at = self .now ()
287
304
# handle message in the main thread's event loop
288
305
self .main_loop .add_callback (self .parent .on_server_message , message , self )
289
- self .from_lsp .task_done ()
290
306
291
307
292
308
class LanguageServerSessionStdio (LanguageServerSessionBase ):
0 commit comments