@@ -116,6 +116,8 @@ export class SettingsUIManager {
116
116
}
117
117
) ;
118
118
}
119
+ this . _canonical = null ;
120
+ this . _original = null ;
119
121
this . _validationAttempt = 0 ;
120
122
this . _lastValidation = null ;
121
123
this . _lastUserServerSettings = null ;
@@ -132,8 +134,6 @@ export class SettingsUIManager {
132
134
* is performed on settingRegistry with regard to pluginId.
133
135
*/
134
136
async setupSchemaForUI ( pluginId : string ) : Promise < void > {
135
- let canonical : ISettingRegistry . ISchema | null ;
136
- let original : ISettingRegistry . ISchema | null = null ;
137
137
const languageServerManager = this . options . languageServerManager ;
138
138
/**
139
139
* Populate the plugin's schema defaults.
@@ -234,87 +234,44 @@ export class SettingsUIManager {
234
234
schema . properties ! . language_servers . properties = knownServersConfig ;
235
235
schema . properties ! . language_servers . default = defaults ;
236
236
237
- const lastValidation = this . _lastValidation ;
238
- // do not re-validate if neither schema, nor user settings changed
239
- if (
240
- lastValidation === null ||
241
- lastValidation . rawUserSettings !== plugin . raw ||
242
- ! JSONExt . deepEqual ( lastValidation . schema , schema )
243
- ) {
244
- // test if we can apply the schema without causing validation error
245
- // (is the configuration held by the user compatible with the schema?)
246
- this . _validationAttempt += 1 ;
247
- // the validator will parse raw plugin data into this object;
248
- // we do not do anything with those right now.
249
- const parsedData = { composite : { } , user : { } } ;
250
- const validationErrors =
251
- this . options . settingRegistry . validator . validateData (
252
- {
253
- // the plugin schema is cached so we have to provide a dummy ID.
254
- id : `lsp-validation-attempt-${ this . _validationAttempt } ` ,
255
- raw : plugin . raw ,
256
- data : parsedData ,
257
- version : plugin . version ,
258
- schema : schema
259
- } ,
260
- true
261
- ) ;
262
-
263
- if ( validationErrors ) {
264
- console . error (
265
- 'LSP server settings validation failed; graphical interface for settings will run in schema-free mode; errors:' ,
266
- validationErrors
267
- ) ;
268
- this . _validationErrors = validationErrors ;
269
- if ( ! original ) {
270
- console . error (
271
- 'Original language servers schema not available to restore non-transformed values.'
272
- ) ;
273
- } else {
274
- if ( ! original . properties ! . language_servers . properties ) {
275
- delete schema . properties ! . language_servers . properties ;
276
- }
277
- if ( ! original . properties ! . language_servers . default ) {
278
- delete schema . properties ! . language_servers . default ;
279
- }
280
- }
281
- }
282
-
283
- this . _lastValidation = {
284
- rawUserSettings : plugin . raw ,
285
- schema : schema
286
- } ;
287
- }
237
+ this . _validateSchemaLater ( plugin , schema ) . catch ( this . console . warn ) ;
288
238
289
239
this . _defaults = defaults ;
290
240
} ;
291
241
292
242
// Transform the plugin object to return different schema than the default.
293
243
this . options . settingRegistry . transform ( pluginId , {
294
244
fetch : plugin => {
295
- // Profiling data:
245
+ // Profiling data (earlier version) :
296
246
// Initial fetch: 61-64 ms
297
247
// Subsequent without change: <1ms
298
248
// Session change: 642 ms.
299
- // 91% spent on `validateData()`
300
- // 10% in addSchema().
249
+ // 91% spent on `validateData()` of which 10% in addSchema().
301
250
// 1.8% spent on `deepCopy()`
302
251
// 1.79% spend on other tasks in `populate()`
303
-
304
- if ( ! original ) {
305
- original = JSONExt . deepCopy ( plugin . schema ) ;
252
+ // There is a limit on the transformation time, and failing to transform
253
+ // in the default 1 second means that no settigns whatsoever are available.
254
+ // Therefore validation in `populate()` was moved into an async function;
255
+ // this means that we need to trigger re-load of settings
256
+ // if there validation errors.
257
+
258
+ // Only store the original schema the first time.
259
+ if ( ! this . _original ) {
260
+ this . _original = JSONExt . deepCopy ( plugin . schema ) ;
306
261
}
307
- // Only override the canonical schema the first time.
308
- if ( ! canonical ) {
309
- canonical = JSONExt . deepCopy ( plugin . schema ) ;
310
- populate ( plugin , canonical ) ;
262
+ // Only override the canonical schema the first time (or after reset) .
263
+ if ( ! this . _canonical ) {
264
+ this . _canonical = JSONExt . deepCopy ( plugin . schema ) ;
265
+ populate ( plugin , this . _canonical ) ;
311
266
}
312
267
313
268
return {
314
269
data : plugin . data ,
315
270
id : plugin . id ,
316
271
raw : plugin . raw ,
317
- schema : canonical ,
272
+ schema : this . _validationErrors . length
273
+ ? this . _original
274
+ : this . _canonical ,
318
275
version : plugin . version
319
276
} ;
320
277
} ,
@@ -375,11 +332,85 @@ export class SettingsUIManager {
375
332
// note: has to be after transform is called for the first time to avoid
376
333
// race condition, see https://github.com/jupyterlab/jupyterlab/issues/12978
377
334
languageServerManager . sessionsChanged . connect ( async ( ) => {
378
- canonical = null ;
335
+ this . _canonical = null ;
379
336
await this . options . settingRegistry . reload ( pluginId ) ;
380
337
} ) ;
381
338
}
382
339
340
+ private _wasPreviouslyValidated (
341
+ plugin : ISettingRegistry . IPlugin ,
342
+ schema : ISettingRegistry . ISchema
343
+ ) {
344
+ return (
345
+ this . _lastValidation !== null &&
346
+ this . _lastValidation . rawUserSettings === plugin . raw &&
347
+ JSONExt . deepEqual ( this . _lastValidation . schema , schema )
348
+ ) ;
349
+ }
350
+
351
+ /**
352
+ * Validate user settings from plugin against provided schema,
353
+ * asynchronously to avoid blocking the main thread.
354
+ * Stores validation reult in `this._validationErrors`.
355
+ */
356
+ private async _validateSchemaLater (
357
+ plugin : ISettingRegistry . IPlugin ,
358
+ schema : ISettingRegistry . ISchema
359
+ ) {
360
+ // do not re-validate if neither schema, nor user settings changed
361
+ if ( this . _wasPreviouslyValidated ( plugin , schema ) ) {
362
+ return ;
363
+ }
364
+ // test if we can apply the schema without causing validation error
365
+ // (is the configuration held by the user compatible with the schema?)
366
+ this . _validationAttempt += 1 ;
367
+ // the validator will parse raw plugin data into this object;
368
+ // we do not do anything with those right now.
369
+ const parsedData = { composite : { } , user : { } } ;
370
+ const validationErrors =
371
+ this . options . settingRegistry . validator . validateData (
372
+ {
373
+ // the plugin schema is cached so we have to provide a dummy ID;
374
+ // can be simplified once https://github.com/jupyterlab/jupyterlab/issues/12978 is fixed.
375
+ id : `lsp-validation-attempt-${ this . _validationAttempt } ` ,
376
+ raw : plugin . raw ,
377
+ data : parsedData ,
378
+ version : plugin . version ,
379
+ schema : schema
380
+ } ,
381
+ true
382
+ ) ;
383
+
384
+ this . _lastValidation = {
385
+ rawUserSettings : plugin . raw ,
386
+ schema : schema
387
+ } ;
388
+
389
+ if ( validationErrors ) {
390
+ console . error (
391
+ 'LSP server settings validation failed; graphical interface for settings will run in schema-free mode; errors:' ,
392
+ validationErrors
393
+ ) ;
394
+ this . _validationErrors = validationErrors ;
395
+ if ( ! this . _original ) {
396
+ console . error (
397
+ 'Original language servers schema not available to restore non-transformed values.'
398
+ ) ;
399
+ } else {
400
+ if ( ! this . _original . properties ! . language_servers . properties ) {
401
+ delete schema . properties ! . language_servers . properties ;
402
+ }
403
+ if ( ! this . _original . properties ! . language_servers . default ) {
404
+ delete schema . properties ! . language_servers . default ;
405
+ }
406
+ }
407
+
408
+ // Reload settings to use non-restrictive schema; this requires fixing
409
+ // https://github.com/jupyterlab/jupyterlab/issues/12978 upstream to work.
410
+ await this . options . settingRegistry . reload ( plugin . id ) ;
411
+ }
412
+ }
413
+
383
414
private async _warnConflicts ( conflicts : SettingsMergeConflicts ) {
384
415
showDialog ( {
385
416
body : renderCollapseConflicts ( {
@@ -454,4 +485,6 @@ export class SettingsUIManager {
454
485
private _lastValidation : IValidationData | null ;
455
486
private _lastUserServerSettings : LanguageServerSettings | null ;
456
487
private _lastUserServerSettingsDoted : LanguageServerSettings | null ;
488
+ private _canonical : ISettingRegistry . ISchema | null ;
489
+ private _original : ISettingRegistry . ISchema | null ;
457
490
}
0 commit comments