@@ -22,6 +22,8 @@ import { DataBinding } from './dataBinding';
22
22
import minimatch = require( "minimatch" ) ;
23
23
import * as logger from '../logger' ;
24
24
import { updateLanguageConfigurations } from './extension' ;
25
+ import { CustomConfigurationProvider , SourceFileConfigurationItem } from '../api' ;
26
+ import { CancellationTokenSource } from 'vscode' ;
25
27
import { SettingsTracker , getTracker } from './settingsTracker' ;
26
28
27
29
let ui : UI ;
@@ -86,6 +88,10 @@ interface DecorationRangesPair {
86
88
ranges : vscode . Range [ ] ;
87
89
}
88
90
91
+ interface CustomConfigurationParams {
92
+ configurationItems : SourceFileConfigurationItem [ ] ;
93
+ }
94
+
89
95
// Requests
90
96
const NavigationListRequest : RequestType < TextDocumentIdentifier , string , void , void > = new RequestType < TextDocumentIdentifier , string , void , void > ( 'cpptools/requestNavigationList' ) ;
91
97
const GoToDeclarationRequest : RequestType < void , void , void , void > = new RequestType < void , void , void , void > ( 'cpptools/goToDeclaration' ) ;
@@ -105,6 +111,7 @@ const ChangeFolderSettingsNotification: NotificationType<FolderSettingsParams, v
105
111
const ChangeCompileCommandsNotification : NotificationType < FileChangedParams , void > = new NotificationType < FileChangedParams , void > ( 'cpptools/didChangeCompileCommands' ) ;
106
112
const ChangeSelectedSettingNotification : NotificationType < FolderSelectedSettingParams , void > = new NotificationType < FolderSelectedSettingParams , void > ( 'cpptools/didChangeSelectedSetting' ) ;
107
113
const IntervalTimerNotification : NotificationType < void , void > = new NotificationType < void , void > ( 'cpptools/onIntervalTimer' ) ;
114
+ const CustomConfigurationNotification : NotificationType < CustomConfigurationParams , void > = new NotificationType < CustomConfigurationParams , void > ( 'cpptools/didChangeCustomConfiguration' ) ;
108
115
109
116
// Notifications from the server
110
117
const ReloadWindowNotification : NotificationType < void , void > = new NotificationType < void , void > ( 'cpptools/reloadWindow' ) ;
@@ -138,13 +145,19 @@ export interface Client {
138
145
TrackedDocuments : Set < vscode . TextDocument > ;
139
146
onDidChangeSettings ( ) : void ;
140
147
onDidChangeVisibleTextEditors ( editors : vscode . TextEditor [ ] ) : void ;
148
+ onDidChangeCustomConfigurations ( provider : CustomConfigurationProvider ) : void ;
141
149
takeOwnership ( document : vscode . TextDocument ) : void ;
150
+ runBlockingTask < T > ( task : Thenable < T > ) : Thenable < T > ;
151
+ runBlockingThenableWithTimeout ( thenable : ( ) => Thenable < any > , ms : number , tokenSource ?: CancellationTokenSource ) : Thenable < any > ;
152
+ requestWhenReady ( request : ( ) => Thenable < any > ) : Thenable < any > ;
153
+ notifyWhenReady ( notify : ( ) => void ) : void ;
142
154
requestGoToDeclaration ( ) : Thenable < void > ;
143
155
requestSwitchHeaderSource ( rootPath : string , fileName : string ) : Thenable < string > ;
144
156
requestNavigationList ( document : vscode . TextDocument ) : Thenable < string > ;
145
157
activeDocumentChanged ( document : vscode . TextDocument ) : void ;
146
158
activate ( ) : void ;
147
159
selectionChanged ( selection : vscode . Position ) : void ;
160
+ sendCustomConfigurations ( configs : SourceFileConfigurationItem [ ] ) : void ;
148
161
resetDatabase ( ) : void ;
149
162
deactivate ( ) : void ;
150
163
pauseParsing ( ) : void ;
@@ -215,12 +228,14 @@ class DefaultClient implements Client {
215
228
}
216
229
217
230
/**
218
- * All public methods on this class must be guarded by the "onReady " promise. Requests and notifications received before the client is
219
- * ready are executed after this promise is resolved.
231
+ * All public methods on this class must be guarded by the "pendingTask " promise. Requests and notifications received before the task is
232
+ * complete are executed after this promise is resolved.
220
233
* @see requestWhenReady<T>(request)
221
234
* @see notifyWhenReady(notify)
222
235
*/
223
- private onReadyPromise : Thenable < void > ;
236
+
237
+ private pendingTask : Thenable < any > ;
238
+ private pendingRequests : number = 0 ;
224
239
225
240
constructor ( allClients : ClientCollection , workspaceFolder ?: vscode . WorkspaceFolder ) {
226
241
try {
@@ -232,7 +247,7 @@ class DefaultClient implements Client {
232
247
ui = getUI ( ) ;
233
248
ui . bind ( this ) ;
234
249
235
- this . onReadyPromise = languageClient . onReady ( ) . then ( ( ) => {
250
+ this . runBlockingTask ( languageClient . onReady ( ) ) . then ( ( ) => {
236
251
this . configuration = new configs . CppProperties ( this . RootUri ) ;
237
252
this . configuration . ConfigurationsChanged ( ( e ) => this . onConfigurationsChanged ( e ) ) ;
238
253
this . configuration . SelectionChanged ( ( e ) => this . onSelectedConfigurationChanged ( e ) ) ;
@@ -385,6 +400,23 @@ class DefaultClient implements Client {
385
400
}
386
401
}
387
402
403
+ public onDidChangeCustomConfigurations ( provider : CustomConfigurationProvider ) : void {
404
+ let documentUris : vscode . Uri [ ] = [ ] ;
405
+ this . trackedDocuments . forEach ( document => documentUris . push ( document . uri ) ) ;
406
+
407
+ let tokenSource : CancellationTokenSource = new CancellationTokenSource ( ) ;
408
+
409
+ if ( documentUris . length === 0 ) {
410
+ return ;
411
+ }
412
+
413
+ this . runBlockingThenableWithTimeout ( ( ) => {
414
+ return provider . provideConfigurations ( documentUris , tokenSource . token ) ;
415
+ } , 1000 , tokenSource ) . then ( ( configs : SourceFileConfigurationItem [ ] ) => {
416
+ this . sendCustomConfigurations ( configs ) ;
417
+ } ) ;
418
+ }
419
+
388
420
/**
389
421
* Take ownership of a document that was previously serviced by another client.
390
422
* This process involves sending a textDocument/didOpen message to the server so
@@ -405,24 +437,75 @@ class DefaultClient implements Client {
405
437
}
406
438
407
439
/*************************************************************************************
408
- * wait until the language client is ready for use before attempting to send messages
440
+ * wait until the all pendingTasks are complete (e.g. language client is ready for use)
441
+ * before attempting to send messages
409
442
*************************************************************************************/
443
+ public runBlockingTask ( task : Thenable < any > ) : Thenable < any > {
444
+ if ( this . pendingTask ) {
445
+ return this . requestWhenReady ( ( ) => { return task ; } ) ;
446
+ } else {
447
+ this . pendingTask = task ;
448
+ return task . then ( ( result ) => {
449
+ this . pendingTask = undefined ;
450
+ return result ;
451
+ } , ( error ) => {
452
+ this . pendingTask = undefined ;
453
+ throw error ;
454
+ } ) ;
455
+ }
456
+ }
410
457
411
- private requestWhenReady < T > ( request : ( ) => Thenable < T > ) : Thenable < T > {
412
- if ( this . languageClient ) {
458
+ public runBlockingThenableWithTimeout ( thenable : ( ) => Thenable < any > , ms : number , tokenSource ?: CancellationTokenSource ) : Thenable < any > {
459
+ let timer : NodeJS . Timer ;
460
+ // Create a promise that rejects in <ms> milliseconds
461
+ let timeout : Promise < any > = new Promise ( ( resolve , reject ) => {
462
+ timer = setTimeout ( ( ) => {
463
+ clearTimeout ( timer ) ;
464
+ if ( tokenSource ) {
465
+ tokenSource . cancel ( ) ;
466
+ }
467
+ reject ( "Timed out in " + ms + "ms." ) ;
468
+ } , ms ) ;
469
+ } ) ;
470
+
471
+ // Returns a race between our timeout and the passed in promise
472
+ return this . runBlockingTask ( Promise . race ( [ thenable ( ) , timeout ] ) . then ( ( result : any ) => {
473
+ clearTimeout ( timer ) ;
474
+ return result ;
475
+ } , ( error : any ) => {
476
+ throw error ;
477
+ } ) ) ;
478
+ }
479
+
480
+ public requestWhenReady ( request : ( ) => Thenable < any > ) : Thenable < any > {
481
+ if ( this . pendingTask === undefined ) {
413
482
return request ( ) ;
414
- } else if ( this . isSupported && this . onReadyPromise ) {
415
- return this . onReadyPromise . then ( ( ) => request ( ) ) ;
483
+ } else if ( this . isSupported && this . pendingTask ) {
484
+ this . pendingRequests ++ ;
485
+ return this . pendingTask . then ( ( ) => {
486
+ this . pendingRequests -- ;
487
+ if ( this . pendingRequests === 0 ) {
488
+ this . pendingTask = undefined ;
489
+ }
490
+ return request ( ) ;
491
+ } ) ;
416
492
} else {
417
- return Promise . reject < T > ( "Unsupported client" ) ;
493
+ return Promise . reject ( "Unsupported client" ) ;
418
494
}
419
495
}
420
496
421
- private notifyWhenReady ( notify : ( ) => void ) : void {
422
- if ( this . languageClient ) {
497
+ public notifyWhenReady ( notify : ( ) => void ) : void {
498
+ if ( this . pendingTask === undefined ) {
423
499
notify ( ) ;
424
- } else if ( this . isSupported && this . onReadyPromise ) {
425
- this . onReadyPromise . then ( ( ) => notify ( ) ) ;
500
+ } else if ( this . isSupported && this . pendingTask ) {
501
+ this . pendingRequests ++ ;
502
+ this . pendingTask . then ( ( ) => {
503
+ this . pendingRequests -- ;
504
+ if ( this . pendingRequests === 0 ) {
505
+ this . pendingTask = undefined ;
506
+ }
507
+ notify ( ) ;
508
+ } ) ;
426
509
}
427
510
}
428
511
@@ -763,6 +846,13 @@ class DefaultClient implements Client {
763
846
this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( ChangeCompileCommandsNotification , params ) ) ;
764
847
}
765
848
849
+ public sendCustomConfigurations ( configs : SourceFileConfigurationItem [ ] ) : void {
850
+ let params : CustomConfigurationParams = {
851
+ configurationItems : configs
852
+ } ;
853
+ this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( CustomConfigurationNotification , params ) ) ;
854
+ }
855
+
766
856
/*********************************************
767
857
* command handlers
768
858
*********************************************/
@@ -853,7 +943,13 @@ class NullClient implements Client {
853
943
TrackedDocuments = new Set < vscode . TextDocument > ( ) ;
854
944
onDidChangeSettings ( ) : void { }
855
945
onDidChangeVisibleTextEditors ( editors : vscode . TextEditor [ ] ) : void { }
946
+ onDidChangeCustomConfigurations ( provider : CustomConfigurationProvider ) : void { }
856
947
takeOwnership ( document : vscode . TextDocument ) : void { }
948
+ runBlockingTask < T > ( task : Thenable < T > ) : Thenable < T > { return ; }
949
+ runBlockingThenableWithTimeout ( thenable : ( ) => Thenable < any > , ms : number , tokenSource ?: CancellationTokenSource ) : Thenable < any > { return ; }
950
+ requestWhenReady ( request : ( ) => Thenable < any > ) : Thenable < any > { return ; }
951
+ notifyWhenReady ( notify : ( ) => void ) : void { }
952
+ sendCustomConfigurations ( configs : SourceFileConfigurationItem [ ] ) : void { }
857
953
requestGoToDeclaration ( ) : Thenable < void > { return Promise . resolve ( ) ; }
858
954
requestSwitchHeaderSource ( rootPath : string , fileName : string ) : Thenable < string > { return Promise . resolve ( "" ) ; }
859
955
requestNavigationList ( document : vscode . TextDocument ) : Thenable < string > { return Promise . resolve ( "" ) ; }
0 commit comments