@@ -22,6 +22,8 @@ import { DataBinding } from './dataBinding';
2222import minimatch = require( "minimatch" ) ;
2323import * as logger from '../logger' ;
2424import { updateLanguageConfigurations } from './extension' ;
25+ import { CustomConfigurationProvider , SourceFileConfigurationItem } from '../api' ;
26+ import { CancellationTokenSource } from 'vscode' ;
2527import { SettingsTracker , getTracker } from './settingsTracker' ;
2628
2729let ui : UI ;
@@ -86,6 +88,10 @@ interface DecorationRangesPair {
8688 ranges : vscode . Range [ ] ;
8789}
8890
91+ interface CustomConfigurationParams {
92+ configurationItems : SourceFileConfigurationItem [ ] ;
93+ }
94+
8995// Requests
9096const NavigationListRequest : RequestType < TextDocumentIdentifier , string , void , void > = new RequestType < TextDocumentIdentifier , string , void , void > ( 'cpptools/requestNavigationList' ) ;
9197const GoToDeclarationRequest : RequestType < void , void , void , void > = new RequestType < void , void , void , void > ( 'cpptools/goToDeclaration' ) ;
@@ -105,6 +111,7 @@ const ChangeFolderSettingsNotification: NotificationType<FolderSettingsParams, v
105111const ChangeCompileCommandsNotification : NotificationType < FileChangedParams , void > = new NotificationType < FileChangedParams , void > ( 'cpptools/didChangeCompileCommands' ) ;
106112const ChangeSelectedSettingNotification : NotificationType < FolderSelectedSettingParams , void > = new NotificationType < FolderSelectedSettingParams , void > ( 'cpptools/didChangeSelectedSetting' ) ;
107113const IntervalTimerNotification : NotificationType < void , void > = new NotificationType < void , void > ( 'cpptools/onIntervalTimer' ) ;
114+ const CustomConfigurationNotification : NotificationType < CustomConfigurationParams , void > = new NotificationType < CustomConfigurationParams , void > ( 'cpptools/didChangeCustomConfiguration' ) ;
108115
109116// Notifications from the server
110117const ReloadWindowNotification : NotificationType < void , void > = new NotificationType < void , void > ( 'cpptools/reloadWindow' ) ;
@@ -138,13 +145,19 @@ export interface Client {
138145 TrackedDocuments : Set < vscode . TextDocument > ;
139146 onDidChangeSettings ( ) : void ;
140147 onDidChangeVisibleTextEditors ( editors : vscode . TextEditor [ ] ) : void ;
148+ onDidChangeCustomConfigurations ( provider : CustomConfigurationProvider ) : void ;
141149 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 ;
142154 requestGoToDeclaration ( ) : Thenable < void > ;
143155 requestSwitchHeaderSource ( rootPath : string , fileName : string ) : Thenable < string > ;
144156 requestNavigationList ( document : vscode . TextDocument ) : Thenable < string > ;
145157 activeDocumentChanged ( document : vscode . TextDocument ) : void ;
146158 activate ( ) : void ;
147159 selectionChanged ( selection : vscode . Position ) : void ;
160+ sendCustomConfigurations ( configs : SourceFileConfigurationItem [ ] ) : void ;
148161 resetDatabase ( ) : void ;
149162 deactivate ( ) : void ;
150163 pauseParsing ( ) : void ;
@@ -215,12 +228,14 @@ class DefaultClient implements Client {
215228 }
216229
217230 /**
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.
220233 * @see requestWhenReady<T>(request)
221234 * @see notifyWhenReady(notify)
222235 */
223- private onReadyPromise : Thenable < void > ;
236+
237+ private pendingTask : Thenable < any > ;
238+ private pendingRequests : number = 0 ;
224239
225240 constructor ( allClients : ClientCollection , workspaceFolder ?: vscode . WorkspaceFolder ) {
226241 try {
@@ -232,7 +247,7 @@ class DefaultClient implements Client {
232247 ui = getUI ( ) ;
233248 ui . bind ( this ) ;
234249
235- this . onReadyPromise = languageClient . onReady ( ) . then ( ( ) => {
250+ this . runBlockingTask ( languageClient . onReady ( ) ) . then ( ( ) => {
236251 this . configuration = new configs . CppProperties ( this . RootUri ) ;
237252 this . configuration . ConfigurationsChanged ( ( e ) => this . onConfigurationsChanged ( e ) ) ;
238253 this . configuration . SelectionChanged ( ( e ) => this . onSelectedConfigurationChanged ( e ) ) ;
@@ -385,6 +400,23 @@ class DefaultClient implements Client {
385400 }
386401 }
387402
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+
388420 /**
389421 * Take ownership of a document that was previously serviced by another client.
390422 * This process involves sending a textDocument/didOpen message to the server so
@@ -405,24 +437,75 @@ class DefaultClient implements Client {
405437 }
406438
407439 /*************************************************************************************
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
409442 *************************************************************************************/
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+ }
410457
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 ) {
413482 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+ } ) ;
416492 } else {
417- return Promise . reject < T > ( "Unsupported client" ) ;
493+ return Promise . reject ( "Unsupported client" ) ;
418494 }
419495 }
420496
421- private notifyWhenReady ( notify : ( ) => void ) : void {
422- if ( this . languageClient ) {
497+ public notifyWhenReady ( notify : ( ) => void ) : void {
498+ if ( this . pendingTask === undefined ) {
423499 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+ } ) ;
426509 }
427510 }
428511
@@ -763,6 +846,13 @@ class DefaultClient implements Client {
763846 this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( ChangeCompileCommandsNotification , params ) ) ;
764847 }
765848
849+ public sendCustomConfigurations ( configs : SourceFileConfigurationItem [ ] ) : void {
850+ let params : CustomConfigurationParams = {
851+ configurationItems : configs
852+ } ;
853+ this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( CustomConfigurationNotification , params ) ) ;
854+ }
855+
766856 /*********************************************
767857 * command handlers
768858 *********************************************/
@@ -853,7 +943,13 @@ class NullClient implements Client {
853943 TrackedDocuments = new Set < vscode . TextDocument > ( ) ;
854944 onDidChangeSettings ( ) : void { }
855945 onDidChangeVisibleTextEditors ( editors : vscode . TextEditor [ ] ) : void { }
946+ onDidChangeCustomConfigurations ( provider : CustomConfigurationProvider ) : void { }
856947 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 { }
857953 requestGoToDeclaration ( ) : Thenable < void > { return Promise . resolve ( ) ; }
858954 requestSwitchHeaderSource ( rootPath : string , fileName : string ) : Thenable < string > { return Promise . resolve ( "" ) ; }
859955 requestNavigationList ( document : vscode . TextDocument ) : Thenable < string > { return Promise . resolve ( "" ) ; }
0 commit comments