@@ -121,8 +121,14 @@ def __init__(self, protocol: LanguageServerProtocol) -> None:
121
121
self ._workspace_diagnostics_task : Optional [asyncio .Task [Any ]] = None
122
122
123
123
self ._diagnostics_loop : Optional [asyncio .AbstractEventLoop ] = None
124
+ self ._single_diagnostics_loop : Optional [asyncio .AbstractEventLoop ] = None
125
+
124
126
self ._diagnostics_loop_lock = threading .RLock ()
125
127
self ._diagnostics_started = threading .Event ()
128
+ self ._single_diagnostics_started = threading .Event ()
129
+
130
+ self ._diagnostics_server_thread : Optional [threading .Thread ] = None
131
+ self ._single_diagnostics_server_thread : Optional [threading .Thread ] = None
126
132
127
133
self .parent .on_initialized .add (self .initialized )
128
134
@@ -148,6 +154,15 @@ def diagnostics_loop(self) -> asyncio.AbstractEventLoop:
148
154
149
155
return self ._diagnostics_loop
150
156
157
+ @property
158
+ def single_diagnostics_loop (self ) -> asyncio .AbstractEventLoop :
159
+ if self ._single_diagnostics_loop is None :
160
+ self ._ensure_diagnostics_thread_started ()
161
+
162
+ assert self ._single_diagnostics_loop is not None
163
+
164
+ return self ._single_diagnostics_loop
165
+
151
166
def _run_diagnostics (self ) -> None :
152
167
loop = asyncio .new_event_loop ()
153
168
asyncio .set_event_loop (loop )
@@ -164,17 +179,39 @@ def _run_diagnostics(self) -> None:
164
179
asyncio .set_event_loop (None )
165
180
loop .close ()
166
181
182
+ def _single_run_diagnostics (self ) -> None :
183
+ loop = asyncio .new_event_loop ()
184
+ asyncio .set_event_loop (loop )
185
+ try :
186
+ self ._single_diagnostics_loop = loop
187
+ self ._single_diagnostics_started .set ()
188
+
189
+ loop .slow_callback_duration = 10
190
+
191
+ loop .run_forever ()
192
+ _cancel_all_tasks (loop )
193
+ loop .run_until_complete (loop .shutdown_asyncgens ())
194
+ finally :
195
+ asyncio .set_event_loop (None )
196
+ loop .close ()
197
+
167
198
def _ensure_diagnostics_thread_started (self ) -> None :
168
199
with self ._diagnostics_loop_lock :
169
200
if self ._diagnostics_loop is None :
170
- self ._server_thread = threading .Thread (
201
+ self ._diagnostics_server_thread = threading .Thread (
171
202
name = "diagnostics_worker" , target = self ._run_diagnostics , daemon = True
172
203
)
173
204
174
- self ._server_thread .start ()
205
+ self ._diagnostics_server_thread .start ()
206
+
207
+ self ._single_diagnostics_server_thread = threading .Thread (
208
+ name = "single_diagnostics_worker" , target = self ._single_run_diagnostics , daemon = True
209
+ )
210
+
211
+ self ._single_diagnostics_server_thread .start ()
175
212
176
- if not self ._diagnostics_started .wait (10 ):
177
- raise RuntimeError ("Can't start diagnostics worker thread ." )
213
+ if not self ._diagnostics_started .wait (10 ) or not self . _single_diagnostics_started . wait ( 10 ) :
214
+ raise RuntimeError ("Can't start diagnostics worker threads ." )
178
215
179
216
def extend_capabilities (self , capabilities : ServerCapabilities ) -> None :
180
217
if (
@@ -280,7 +317,6 @@ async def run_workspace_diagnostics(self) -> None:
280
317
281
318
while True :
282
319
try :
283
-
284
320
documents = [
285
321
doc
286
322
for doc in self .parent .documents .documents
@@ -296,8 +332,11 @@ async def run_workspace_diagnostics(self) -> None:
296
332
await asyncio .sleep (1 )
297
333
continue
298
334
335
+ self ._logger .info (f"start collecting workspace diagnostics for { len (documents )} documents" )
336
+
299
337
done_something = False
300
338
339
+ start = time .monotonic ()
301
340
async with self .parent .window .progress (
302
341
"Analyse workspace" , cancellable = False , current = 0 , max = len (documents ) + 1 , start = False
303
342
) as progress :
@@ -324,21 +363,31 @@ async def run_workspace_diagnostics(self) -> None:
324
363
progress .report (current = i + 1 )
325
364
326
365
await self .create_document_diagnostics_task (
327
- document , False , await self .get_diagnostics_mode (document .uri ) == DiagnosticsMode .WORKSPACE
366
+ document ,
367
+ False ,
368
+ False ,
369
+ await self .get_diagnostics_mode (document .uri ) == DiagnosticsMode .WORKSPACE ,
328
370
)
329
371
330
372
if not done_something :
331
373
await asyncio .sleep (1 )
374
+
375
+ self ._logger .info (
376
+ f"collecting workspace diagnostics for for { len (documents )} "
377
+ f"documents takes { time .monotonic () - start } s"
378
+ )
379
+
332
380
except (SystemExit , KeyboardInterrupt , asyncio .CancelledError ):
333
381
raise
334
382
except BaseException as e :
335
383
self ._logger .exception (f"Error in workspace diagnostics loop: { e } " , exc_info = e )
336
384
337
385
def create_document_diagnostics_task (
338
- self , document : TextDocument , debounce : bool = True , send_diagnostics : bool = True
386
+ self , document : TextDocument , single : bool , debounce : bool = True , send_diagnostics : bool = True
339
387
) -> asyncio .Task [Any ]:
340
388
def done (t : asyncio .Task [Any ]) -> None :
341
389
self ._logger .debug (lambda : f"diagnostics for { document } { 'canceled' if t .cancelled () else 'ended' } " )
390
+
342
391
if t .done () and not t .cancelled ():
343
392
ex = t .exception ()
344
393
@@ -372,7 +421,7 @@ async def cancel(t: asyncio.Task[Any]) -> None:
372
421
data .version = document .version
373
422
data .task = create_sub_task (
374
423
self ._get_diagnostics_for_document (document , data , debounce , send_diagnostics ),
375
- loop = self .diagnostics_loop ,
424
+ loop = self .single_diagnostics_loop if single else self . diagnostics_loop ,
376
425
name = f"diagnostics ${ document .uri } " ,
377
426
)
378
427
@@ -444,7 +493,7 @@ async def _text_document_diagnostic(
444
493
if document is None :
445
494
raise JsonRPCErrorException (ErrorCodes .SERVER_CANCELLED , f"Document { text_document !r} not found." )
446
495
447
- self .create_document_diagnostics_task (document )
496
+ self .create_document_diagnostics_task (document , True )
448
497
449
498
return RelatedFullDocumentDiagnosticReport ([])
450
499
except asyncio .CancelledError :
0 commit comments