77import type { ConfigurationItem , DidChangeConfigurationParams , DidChangeConfigurationRegistrationOptions , InitializeParams , InitializedParams } from 'vscode-languageserver-protocol' ;
88import type { ServiceRegistry } from '../service-registry.js' ;
99import type { LangiumSharedCoreServices } from '../services.js' ;
10+ import { Deferred } from '../utils/promise-utils.js' ;
1011
1112/* eslint-disable @typescript-eslint/no-explicit-any */
1213
1314export interface ConfigurationProvider {
1415
16+ /**
17+ * A promise that resolves when the configuration provider is ready to be used.
18+ */
19+ readonly ready : Promise < void > ;
20+
1521 /**
1622 * When used in a language server context, this method is called when the server receives
1723 * the `initialize` request.
@@ -52,14 +58,18 @@ export interface ConfigurationInitializedParams extends InitializedParams {
5258export class DefaultConfigurationProvider implements ConfigurationProvider {
5359
5460 protected readonly serviceRegistry : ServiceRegistry ;
61+ protected readonly _ready = new Deferred < void > ( ) ;
5562 protected settings : Record < string , Record < string , any > > = { } ;
5663 protected workspaceConfig = false ;
57- protected fetchConfiguration : ( ( configurations : ConfigurationItem [ ] ) => Promise < any > ) | undefined ;
5864
5965 constructor ( services : LangiumSharedCoreServices ) {
6066 this . serviceRegistry = services . ServiceRegistry ;
6167 }
6268
69+ get ready ( ) : Promise < void > {
70+ return this . _ready . promise ;
71+ }
72+
6373 initialize ( params : InitializeParams ) : void {
6474 this . workspaceConfig = params . capabilities . workspace ?. configuration ?? false ;
6575 }
@@ -77,35 +87,22 @@ export class DefaultConfigurationProvider implements ConfigurationProvider {
7787 } ) ;
7888 }
7989
80- // params.fetchConfiguration(...) is a function to be provided by the calling language server for the sake of
81- // decoupling this implementation from the concrete LSP implementations, specifically the LSP Connection
82- this . fetchConfiguration = params . fetchConfiguration ;
83-
84- // awaiting the fetch of the initial configuration data must not happen here, because it would block the
85- // initialization of the language server, and may lead to out-of-order processing of subsequent messages from the language client;
86- // fetching the initial configuration in a non-blocking manner might cause a read-before-write problem
87- // in case the workspace initialization relies on that configuration data (which is the case for the grammar language server);
88- // consequently, we fetch the initial configuration data on blocking manner on-demand, when the first configuration value is read,
89- // see below in #getConfiguration(...);
90- }
91- }
90+ if ( params . fetchConfiguration ) {
91+ // params.fetchConfiguration(...) is a function to be provided by the calling language server for the sake of
92+ // decoupling this implementation from the concrete LSP implementations, specifically the LSP Connection
93+ const configToUpdate = this . serviceRegistry . all . map ( lang => < ConfigurationItem > {
94+ // Fetch the configuration changes for all languages
95+ section : this . toSectionName ( lang . LanguageMetaData . languageId )
96+ } ) ;
9297
93- protected async initializeConfiguration ( ) : Promise < void > {
94- if ( this . fetchConfiguration ) {
95- const configToUpdate = this . serviceRegistry . all . map ( lang => < ConfigurationItem > {
96- // Fetch the configuration changes for all languages
97- section : this . toSectionName ( lang . LanguageMetaData . languageId )
98- } ) ;
99-
100- // get workspace configurations (default scope URI)
101- const configs = await this . fetchConfiguration ( configToUpdate ) ;
102- configToUpdate . forEach ( ( conf , idx ) => {
103- this . updateSectionConfiguration ( conf . section ! , configs [ idx ] ) ;
104- } ) ;
105-
106- // reset the 'fetchConfiguration' to 'undefined' again in order to prevent further 'fetch' attempts
107- this . fetchConfiguration = undefined ;
98+ // get workspace configurations (default scope URI)
99+ const configs = await params . fetchConfiguration ( configToUpdate ) ;
100+ configToUpdate . forEach ( ( conf , idx ) => {
101+ this . updateSectionConfiguration ( conf . section ! , configs [ idx ] ) ;
102+ } ) ;
103+ }
108104 }
105+ this . _ready . resolve ( ) ;
109106 }
110107
111108 /**
@@ -134,7 +131,7 @@ export class DefaultConfigurationProvider implements ConfigurationProvider {
134131 * @param configuration Configuration name
135132 */
136133 async getConfiguration ( language : string , configuration : string ) : Promise < any > {
137- await this . initializeConfiguration ( ) ;
134+ await this . ready ;
138135
139136 const sectionName = this . toSectionName ( language ) ;
140137 if ( this . settings [ sectionName ] ) {
0 commit comments