9
9
import { isNgLanguageService , NgLanguageService , PluginConfig } from '@angular/language-service/api' ;
10
10
import * as assert from 'assert' ;
11
11
import * as ts from 'typescript/lib/tsserverlibrary' ;
12
+ import { promisify } from 'util' ;
12
13
import * as lsp from 'vscode-languageserver/node' ;
13
14
14
15
import { ServerOptions } from '../common/initialize' ;
@@ -20,7 +21,7 @@ import {readNgCompletionData, tsCompletionEntryToLspCompletionItem} from './comp
20
21
import { tsDiagnosticToLspDiagnostic } from './diagnostic' ;
21
22
import { resolveAndRunNgcc } from './ngcc' ;
22
23
import { ServerHost } from './server_host' ;
23
- import { filePathToUri , isConfiguredProject , lspPositionToTsPosition , lspRangeToTsPositions , tsTextSpanToLspRange , uriToFilePath } from './utils' ;
24
+ import { filePathToUri , isConfiguredProject , isDebugMode , lspPositionToTsPosition , lspRangeToTsPositions , tsTextSpanToLspRange , uriToFilePath } from './utils' ;
24
25
import { resolve , Version } from './version_provider' ;
25
26
26
27
export interface SessionOptions {
@@ -39,6 +40,7 @@ enum LanguageId {
39
40
40
41
// Empty definition range for files without `scriptInfo`
41
42
const EMPTY_RANGE = lsp . Range . create ( 0 , 0 , 0 , 0 ) ;
43
+ const setImmediateP = promisify ( setImmediate ) ;
42
44
43
45
/**
44
46
* Session is a wrapper around lsp.IConnection, with all the necessary protocol
@@ -293,7 +295,7 @@ export class Session {
293
295
* @param openFiles
294
296
* @param delay time to wait before sending request (milliseconds)
295
297
*/
296
- private triggerDiagnostics ( openFiles : string [ ] , delay : number = 200 ) {
298
+ private triggerDiagnostics ( openFiles : string [ ] , delay : number = 300 ) {
297
299
// Do not immediately send a diagnostics request. Send only after user has
298
300
// stopped typing after the specified delay.
299
301
if ( this . diagnosticsTimeout ) {
@@ -313,19 +315,39 @@ export class Session {
313
315
* Execute diagnostics request for each of the specified `openFiles`.
314
316
* @param openFiles
315
317
*/
316
- private sendPendingDiagnostics ( openFiles : string [ ] ) {
317
- for ( const fileName of openFiles ) {
318
+ private async sendPendingDiagnostics ( openFiles : string [ ] ) {
319
+ for ( let i = 0 ; i < openFiles . length ; ++ i ) {
320
+ const fileName = openFiles [ i ] ;
318
321
const result = this . getLSAndScriptInfo ( fileName ) ;
319
322
if ( ! result ) {
320
323
continue ;
321
324
}
325
+ const label = `getSemanticDiagnostics - ${ fileName } ` ;
326
+ if ( isDebugMode ) {
327
+ console . time ( label ) ;
328
+ }
322
329
const diagnostics = result . languageService . getSemanticDiagnostics ( fileName ) ;
330
+ if ( isDebugMode ) {
331
+ console . timeEnd ( label ) ;
332
+ }
323
333
// Need to send diagnostics even if it's empty otherwise editor state will
324
334
// not be updated.
325
335
this . connection . sendDiagnostics ( {
326
336
uri : filePathToUri ( fileName ) ,
327
337
diagnostics : diagnostics . map ( d => tsDiagnosticToLspDiagnostic ( d , result . scriptInfo ) ) ,
328
338
} ) ;
339
+ if ( this . diagnosticsTimeout ) {
340
+ // There is a pending request to check diagnostics for all open files,
341
+ // so stop this one immediately.
342
+ return ;
343
+ }
344
+ if ( i < openFiles . length - 1 ) {
345
+ // If this is not the last file, yield so that pending I/O events get a
346
+ // chance to run. This will open an opportunity for the server to process
347
+ // incoming requests. The next file will be checked in the next iteration
348
+ // of the event loop.
349
+ await setImmediateP ( ) ;
350
+ }
329
351
}
330
352
}
331
353
0 commit comments