@@ -145,6 +145,34 @@ const InactiveRegionNotification: NotificationType<InactiveRegionParams, void>
145145const CompileCommandsPathsNotification :NotificationType < CompileCommandsPaths , void > = new NotificationType < CompileCommandsPaths , void > ( 'cpptools/compileCommandsPaths' ) ;
146146const UpdateClangFormatPathNotification :NotificationType < string , void > = new NotificationType < string , void > ( 'cpptools/updateClangFormatPath' ) ;
147147
148+ class BlockingTask < T > {
149+ private dependency :BlockingTask < any > ;
150+ private done :boolean = false ;
151+ private promise :Promise < T > ;
152+
153+ constructor ( task :( ) => T , dependency ?:BlockingTask < any > ) {
154+ this . promise = new Promise < T > ( async ( resolve , reject ) => {
155+ try {
156+ let result :T = await task ( ) ;
157+ resolve ( result ) ;
158+ this . done = true ;
159+ } catch ( err ) {
160+ reject ( err ) ;
161+ this . done = true ;
162+ }
163+ } ) ;
164+ this . dependency = dependency ;
165+ }
166+
167+ public get Done ( ) :boolean {
168+ return this . done && ( ! this . dependency || this . dependency . Done ) ;
169+ }
170+
171+ public then ( onSucceeded :( value :T ) => any , onRejected :( err ) => any ) :Promise < any > {
172+ return this . promise . then ( onSucceeded , onRejected ) ;
173+ }
174+ }
175+
148176let failureMessageShown :boolean = false ;
149177
150178interface ClientModel {
@@ -174,7 +202,6 @@ export interface Client {
174202getCurrentConfigName ( ) :Thenable < string > ;
175203takeOwnership ( document :vscode . TextDocument ) :void ;
176204queueTask < T > ( task :( ) => Thenable < T > ) :Thenable < T > ;
177- queueTaskWithTimeout ( thenable :( ) => Thenable < any > , ms :number , tokenSource ?:CancellationTokenSource ) :Thenable < any > ;
178205requestWhenReady ( request :( ) => Thenable < any > ) :Thenable < any > ;
179206notifyWhenReady ( notify :( ) => void ) :void ;
180207requestGoToDeclaration ( ) :Thenable < void > ;
@@ -183,8 +210,6 @@ export interface Client {
183210activeDocumentChanged ( document :vscode . TextDocument ) :void ;
184211activate ( ) :void ;
185212selectionChanged ( selection :Range ) :void ;
186- sendCustomConfigurations ( configs :any ) :void ;
187- sendCustomBrowseConfiguration ( config :any ) :Thenable < void > ;
188213resetDatabase ( ) :void ;
189214deactivate ( ) :void ;
190215pauseParsing ( ) :void ;
@@ -267,8 +292,7 @@ class DefaultClient implements Client {
267292 *@see notifyWhenReady(notify)
268293 */
269294
270- private pendingTask :Thenable < any > ;
271- private pendingRequests :number = 0 ;
295+ private pendingTask :BlockingTask < void > ;
272296
273297constructor ( allClients :ClientCollection , workspaceFolder ?:vscode . WorkspaceFolder ) {
274298try {
@@ -281,7 +305,7 @@ class DefaultClient implements Client {
281305ui . bind ( this ) ;
282306
283307// requests/notifications are deferred until this.languageClient is set.
284- this . queueTask ( ( ) => languageClient . onReady ( ) . then (
308+ this . queueBlockingTask ( ( ) => languageClient . onReady ( ) . then (
285309( ) => {
286310this . configuration = new configs . CppProperties ( this . RootUri ) ;
287311this . configuration . ConfigurationsChanged ( ( e ) => this . onConfigurationsChanged ( e ) ) ;
@@ -522,10 +546,10 @@ class DefaultClient implements Client {
522546
523547public updateCustomBrowseConfiguration ( requestingProvider ?:CustomConfigurationProvider1 ) :Thenable < void > {
524548return this . notifyWhenReady ( ( ) => {
525- console . log ( "updateCustomBrowseConfiguration" ) ;
526549if ( ! this . configurationProvider ) {
527550return ;
528551}
552+ console . log ( "updateCustomBrowseConfiguration" ) ;
529553let currentProvider :CustomConfigurationProvider1 = getCustomConfigProviders ( ) . get ( this . configurationProvider ) ;
530554if ( ! currentProvider || ( requestingProvider && requestingProvider . extensionId !== currentProvider . extensionId ) ) {
531555return ;
@@ -553,10 +577,10 @@ class DefaultClient implements Client {
553577public async provideCustomConfiguration ( document :vscode . TextDocument ) :Promise < void > {
554578let tokenSource :CancellationTokenSource = new CancellationTokenSource ( ) ;
555579let providers :CustomConfigurationProviderCollection = getCustomConfigProviders ( ) ;
556- console . log ( "provideCustomConfiguration" ) ;
557580if ( providers . size === 0 ) {
558581return Promise . resolve ( ) ;
559582}
583+ console . log ( "provideCustomConfiguration" ) ;
560584let providerId :string | undefined = await this . getCustomConfigurationProviderId ( ) ;
561585if ( ! providerId ) {
562586return Promise . resolve ( ) ;
@@ -588,7 +612,7 @@ class DefaultClient implements Client {
588612return this . queueTaskWithTimeout ( provideConfigurationAsync , configProviderTimeout , tokenSource ) . then (
589613( configs :SourceFileConfigurationItem [ ] ) => {
590614if ( configs && configs . length > 0 ) {
591- this . sendCustomConfigurations ( configs ) ;
615+ this . sendCustomConfigurations ( configs , true ) ;
592616}
593617} ,
594618( err ) => {
@@ -657,32 +681,39 @@ class DefaultClient implements Client {
657681if ( this . isSupported ) {
658682let nextTask :( ) => Thenable < any > = async ( ) => {
659683try {
660- let result :any = await task ( ) ;
661- this . pendingRequests -- ;
662- return result ;
684+ return await task ( ) ;
663685} catch ( err ) {
664686console . error ( err ) ;
665- this . pendingRequests -- ;
666687throw err ;
667688}
668689} ;
669690
670- console . assert ( this . pendingRequests >= 0 ) ;
671- if ( this . pendingRequests === 0 ) {
672- this . pendingRequests ++ ;
673- this . pendingTask = nextTask ( ) ;
674- return this . pendingTask ;
675- } else {
691+ if ( this . pendingTask && ! this . pendingTask . Done ) {
676692// We don't want the queue to stall because of a rejected promise.
677- this . pendingRequests ++ ;
678693return this . pendingTask . then ( nextTask , nextTask ) ;
694+ } else {
695+ this . pendingTask = undefined ;
696+ return nextTask ( ) ;
679697}
680698} else {
681699return Promise . reject ( "Unsupported client" ) ;
682700}
683701}
684702
685- public queueTaskWithTimeout ( task :( ) => Thenable < any > , ms :number , cancelToken ?:CancellationTokenSource ) :Thenable < any > {
703+ /**
704+ * Queue a task that blocks all future tasks until it completes. This is currently only intended to be used
705+ * during language client startup and for custom configuration providers.
706+ *@param task The task that blocks all future tasks
707+ */
708+ private queueBlockingTask ( task :( ) => Thenable < void > ) :Thenable < void > {
709+ if ( this . isSupported ) {
710+ this . pendingTask = new BlockingTask < void > ( task , this . pendingTask ) ;
711+ } else {
712+ return Promise . reject ( "Unsupported client" ) ;
713+ }
714+ }
715+
716+ private queueTaskWithTimeout ( task :( ) => Thenable < any > , ms :number , cancelToken ?:CancellationTokenSource ) :Thenable < any > {
686717let timer :NodeJS . Timer ;
687718// Create a promise that rejects in <ms> milliseconds
688719let timeout :( ) => Promise < any > = ( ) => new Promise ( ( resolve , reject ) => {
@@ -713,11 +744,16 @@ class DefaultClient implements Client {
713744return this . queueTask ( request ) ;
714745}
715746
716- public notifyWhenReady ( notify :( ) => void ) :Thenable < void > {
717- return this . queueTask ( ( ) => new Promise ( resolve => {
747+ public notifyWhenReady ( notify :( ) => void , blockingTask ?: boolean ) :Thenable < void > {
748+ let task : ( ) => Thenable < void > = ( ) => new Promise ( resolve => {
718749notify ( ) ;
719750resolve ( ) ;
720- } ) ) ;
751+ } ) ;
752+ if ( blockingTask ) {
753+ return this . queueBlockingTask ( task ) ;
754+ } else {
755+ return this . queueTask ( task ) ;
756+ }
721757}
722758
723759/**
@@ -1117,7 +1153,7 @@ class DefaultClient implements Client {
11171153util . isOptionalArrayOfString ( input . configuration . forcedInclude ) ) ;
11181154}
11191155
1120- public sendCustomConfigurations ( configs :any ) :void {
1156+ private sendCustomConfigurations ( configs :any , blockingTask ?: boolean ) :void {
11211157// configs is marked as 'any' because it is untrusted data coming from a 3rd-party. We need to sanitize it before sending it to the language server.
11221158if ( ! configs || ! ( configs instanceof Array ) ) {
11231159console . warn ( "discarding invalid SourceFileConfigurationItems[]: " + configs ) ;
@@ -1155,10 +1191,10 @@ class DefaultClient implements Client {
11551191let params :CustomConfigurationParams = {
11561192configurationItems :sanitized
11571193} ;
1158- this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( CustomConfigurationNotification , params ) ) ;
1194+ this . notifyWhenReady ( ( ) => this . languageClient . sendNotification ( CustomConfigurationNotification , params ) , blockingTask ) ;
11591195}
11601196
1161- public sendCustomBrowseConfiguration ( config :any ) :Thenable < void > {
1197+ private sendCustomBrowseConfiguration ( config :any ) :Thenable < void > {
11621198// config is marked as 'any' because it is untrusted data coming from a 3rd-party. We need to sanitize it before sending it to the language server.
11631199if ( ! config || config instanceof Array ) {
11641200console . warn ( "discarding invalid WorkspaceBrowseConfiguration: " + config ) ;
@@ -1307,11 +1343,8 @@ class NullClient implements Client {
13071343getCurrentConfigName ( ) :Thenable < string > { return Promise . resolve ( "" ) ; }
13081344takeOwnership ( document :vscode . TextDocument ) :void { }
13091345queueTask < T > ( task :( ) => Thenable < T > ) :Thenable < T > { return task ( ) ; }
1310- queueTaskWithTimeout ( task :( ) => Thenable < any > , ms :number , tokenSource ?:CancellationTokenSource ) :Thenable < any > { return task ( ) ; }
13111346requestWhenReady ( request :( ) => Thenable < any > ) :Thenable < any > { return ; }
13121347notifyWhenReady ( notify :( ) => void ) :void { }
1313- sendCustomConfigurations ( configs :any ) :void { }
1314- sendCustomBrowseConfiguration ( config :any ) :Thenable < void > { return Promise . resolve ( ) ; }
13151348requestGoToDeclaration ( ) :Thenable < void > { return Promise . resolve ( ) ; }
13161349requestSwitchHeaderSource ( rootPath :string , fileName :string ) :Thenable < string > { return Promise . resolve ( "" ) ; }
13171350requestNavigationList ( document :vscode . TextDocument ) :Thenable < string > { return Promise . resolve ( "" ) ; }