0
# Preferences and Configuration
1
2
Theia's preference system provides type-safe preference management with schema definition, multiple providers, and runtime configuration changes for customizable applications.
3
4
## Capabilities
5
6
### Preference Service
7
8
Core service for accessing and modifying application preferences.
9
10
```typescript { .api }
11
/**
12
* Service for managing application preferences
13
*/
14
interface PreferenceService {
15
/**
16
* Get preference value
17
* @param preferenceName - Preference identifier
18
* @param defaultValue - Default value if preference not set
19
* @returns Preference value or default
20
*/
21
get<T>(preferenceName: string, defaultValue?: T): T | undefined;
22
23
/**
24
* Set preference value
25
* @param preferenceName - Preference identifier
26
* @param value - Value to set
27
* @returns Promise that resolves when preference is saved
28
*/
29
set(preferenceName: string, value: any): Promise<void>;
30
31
/**
32
* Check if preference has been set
33
* @param preferenceName - Preference identifier
34
* @returns True if preference exists
35
*/
36
has(preferenceName: string): boolean;
37
38
/**
39
* Remove preference
40
* @param preferenceName - Preference identifier
41
* @returns Promise that resolves when preference is removed
42
*/
43
unset(preferenceName: string): Promise<void>;
44
45
/**
46
* Event fired when preferences change
47
*/
48
readonly onPreferenceChanged: Event<PreferenceChangeEvent<any>>;
49
50
/**
51
* Event fired when preferences are about to change
52
*/
53
readonly onPreferencesChanged: Event<PreferenceChanges>;
54
}
55
56
/**
57
* Service token for PreferenceService
58
*/
59
const PreferenceService: symbol;
60
```
61
62
**Usage Example:**
63
64
```typescript
65
import { inject, injectable } from "@theia/core";
66
import { PreferenceService } from "@theia/core/lib/browser";
67
68
@injectable()
69
export class MyConfigurableService {
70
constructor(
71
@inject(PreferenceService) private readonly preferences: PreferenceService
72
) {
73
// Listen for preference changes
74
this.preferences.onPreferenceChanged(event => {
75
if (event.preferenceName === 'myExtension.enableFeature') {
76
this.updateFeatureState(event.newValue);
77
}
78
});
79
}
80
81
private updateFeatureState(enabled: boolean): void {
82
console.log(`Feature ${enabled ? 'enabled' : 'disabled'}`);
83
}
84
85
async configure(): Promise<void> {
86
// Get preference with default
87
const theme = this.preferences.get('workbench.colorTheme', 'dark');
88
89
// Check if preference exists
90
if (!this.preferences.has('myExtension.initialized')) {
91
await this.preferences.set('myExtension.initialized', true);
92
}
93
94
// Get typed preference
95
const maxFiles = this.preferences.get<number>('files.maxOpenFiles', 50);
96
}
97
}
98
```
99
100
### Preference Schema
101
102
Define preference schemas with validation, types, and UI metadata.
103
104
```typescript { .api }
105
/**
106
* Preference schema definition
107
*/
108
interface PreferenceSchema {
109
[name: string]: PreferenceSchemaProperty;
110
}
111
112
/**
113
* Individual preference property definition
114
*/
115
interface PreferenceSchemaProperty {
116
/** Property type */
117
type?: 'boolean' | 'string' | 'number' | 'integer' | 'array' | 'object';
118
119
/** Default value */
120
default?: any;
121
122
/** Human-readable description */
123
description?: string;
124
125
/** Allowed enum values */
126
enum?: any[];
127
128
/** Enum descriptions */
129
enumDescriptions?: string[];
130
131
/** Minimum value (numbers) */
132
minimum?: number;
133
134
/** Maximum value (numbers) */
135
maximum?: number;
136
137
/** Array item type */
138
items?: PreferenceSchemaProperty;
139
140
/** Object property definitions */
141
properties?: { [key: string]: PreferenceSchemaProperty };
142
143
/** Additional properties allowed */
144
additionalProperties?: boolean | PreferenceSchemaProperty;
145
146
/** Preference scope */
147
scope?: PreferenceScope;
148
149
/** UI order hint */
150
order?: number;
151
152
/** Deprecation message */
153
deprecationMessage?: string;
154
}
155
156
/**
157
* Preference scope enumeration
158
*/
159
enum PreferenceScope {
160
/** User-wide preferences */
161
User = 1,
162
163
/** Workspace-specific preferences */
164
Workspace = 2,
165
166
/** Folder-specific preferences */
167
Folder = 3
168
}
169
```
170
171
**Usage Example:**
172
173
```typescript
174
import { PreferenceSchema } from "@theia/core/lib/browser";
175
176
export const myExtensionPreferenceSchema: PreferenceSchema = {
177
'myExtension.enableFeature': {
178
type: 'boolean',
179
default: true,
180
description: 'Enable the special feature',
181
scope: PreferenceScope.User
182
},
183
184
'myExtension.maxRetries': {
185
type: 'integer',
186
default: 3,
187
minimum: 1,
188
maximum: 10,
189
description: 'Maximum number of retry attempts'
190
},
191
192
'myExtension.theme': {
193
type: 'string',
194
default: 'auto',
195
enum: ['light', 'dark', 'auto'],
196
enumDescriptions: [
197
'Light theme',
198
'Dark theme',
199
'Auto-detect from system'
200
],
201
description: 'Color theme preference'
202
},
203
204
'myExtension.excludePatterns': {
205
type: 'array',
206
items: {
207
type: 'string'
208
},
209
default: ['*.tmp', '*.log'],
210
description: 'File patterns to exclude'
211
}
212
};
213
```
214
215
### Preference Proxy
216
217
Type-safe preference access with automatic change detection.
218
219
```typescript { .api }
220
/**
221
* Type-safe preference proxy
222
*/
223
class PreferenceProxy<T> {
224
/**
225
* Event fired when preferences change
226
*/
227
readonly onPreferenceChanged: Event<PreferenceChangeEvent<T>>;
228
229
/**
230
* Get all preference values
231
* @returns Current preference values
232
*/
233
getPreferences(): T;
234
235
/**
236
* Access preferences like object properties
237
*/
238
[K in keyof T]: T[K];
239
}
240
241
/**
242
* Create preference proxy
243
* @param preferences - Preference service
244
* @param schema - Preference schema
245
* @returns Type-safe preference proxy
246
*/
247
function createPreferenceProxy<T>(
248
preferences: PreferenceService,
249
schema: PreferenceSchema
250
): PreferenceProxy<T>;
251
```
252
253
**Usage Example:**
254
255
```typescript
256
interface MyExtensionConfig {
257
enableFeature: boolean;
258
maxRetries: number;
259
theme: 'light' | 'dark' | 'auto';
260
excludePatterns: string[];
261
}
262
263
@injectable()
264
export class MyConfigurableService {
265
private readonly config: PreferenceProxy<MyExtensionConfig>;
266
267
constructor(
268
@inject(PreferenceService) preferences: PreferenceService
269
) {
270
this.config = createPreferenceProxy(preferences, myExtensionPreferenceSchema);
271
272
// Listen for specific preference changes
273
this.config.onPreferenceChanged(event => {
274
if (event.preferenceName === 'myExtension.enableFeature') {
275
this.handleFeatureToggle(event.newValue);
276
}
277
});
278
}
279
280
private handleFeatureToggle(enabled: boolean): void {
281
// Type-safe access to preferences
282
console.log(`Feature ${enabled ? 'enabled' : 'disabled'}`);
283
console.log(`Max retries: ${this.config.maxRetries}`);
284
console.log(`Theme: ${this.config.theme}`);
285
console.log(`Exclude patterns: ${this.config.excludePatterns.join(', ')}`);
286
}
287
}
288
```
289
290
### Preference Providers
291
292
Extensible preference provider system for different preference sources.
293
294
```typescript { .api }
295
/**
296
* Preference provider interface
297
*/
298
interface PreferenceProvider {
299
/**
300
* Get preference value
301
* @param preferenceName - Preference name
302
* @returns Preference value or undefined
303
*/
304
get<T>(preferenceName: string): T | undefined;
305
306
/**
307
* Set preference value
308
* @param preferenceName - Preference name
309
* @param value - Value to set
310
* @returns Promise that resolves when saved
311
*/
312
set(preferenceName: string, value: any): Promise<boolean>;
313
314
/**
315
* Get all preference names
316
* @returns Array of preference names
317
*/
318
getPreferenceNames(): string[];
319
320
/**
321
* Event fired when preferences change
322
*/
323
readonly onDidPreferencesChanged: Event<PreferenceChanges>;
324
325
/**
326
* Provider domain (user, workspace, folder)
327
*/
328
readonly domain: PreferenceDomain;
329
}
330
331
/**
332
* Preference domain types
333
*/
334
type PreferenceDomain = 'user' | 'workspace' | 'folder';
335
336
/**
337
* Service token for PreferenceProvider
338
*/
339
const PreferenceProvider: symbol;
340
```
341
342
### Configuration Files
343
344
Support for various configuration file formats.
345
346
```typescript { .api }
347
/**
348
* JSON preference provider for .json config files
349
*/
350
class JsonPreferenceProvider implements PreferenceProvider {
351
constructor(uri: URI, scope: PreferenceScope);
352
353
get<T>(preferenceName: string): T | undefined;
354
set(preferenceName: string, value: any): Promise<boolean>;
355
getPreferenceNames(): string[];
356
357
readonly onDidPreferencesChanged: Event<PreferenceChanges>;
358
readonly domain: PreferenceDomain;
359
}
360
361
/**
362
* Workspace preference provider
363
*/
364
class WorkspacePreferenceProvider extends JsonPreferenceProvider {
365
constructor(workspaceUri: URI);
366
}
367
368
/**
369
* User preference provider
370
*/
371
class UserPreferenceProvider extends JsonPreferenceProvider {
372
constructor();
373
}
374
```
375
376
## Events and Changes
377
378
### Preference Change Events
379
380
Detailed information about preference changes.
381
382
```typescript { .api }
383
/**
384
* Event fired when a preference changes
385
*/
386
interface PreferenceChangeEvent<T> {
387
/** Name of the preference that changed */
388
readonly preferenceName: string;
389
390
/** New preference value */
391
readonly newValue: T | undefined;
392
393
/** Previous preference value */
394
readonly oldValue: T | undefined;
395
396
/** Preference scope that changed */
397
readonly scope: PreferenceScope;
398
399
/** Domain where change occurred */
400
readonly domain: PreferenceDomain;
401
}
402
403
/**
404
* Collection of preference changes
405
*/
406
interface PreferenceChanges {
407
[preferenceName: string]: PreferenceChange;
408
}
409
410
/**
411
* Individual preference change
412
*/
413
interface PreferenceChange {
414
/** New value */
415
readonly newValue: any;
416
417
/** Old value */
418
readonly oldValue: any;
419
420
/** Change scope */
421
readonly scope: PreferenceScope;
422
}
423
```
424
425
## Advanced Usage
426
427
### Custom Preference Providers
428
429
Create custom providers for specialized preference sources.
430
431
```typescript
432
import { injectable } from "@theia/core";
433
import { PreferenceProvider, PreferenceChanges } from "@theia/core/lib/browser";
434
435
@injectable()
436
export class DatabasePreferenceProvider implements PreferenceProvider {
437
readonly domain = 'user';
438
private readonly onDidPreferencesChangedEmitter = new Emitter<PreferenceChanges>();
439
readonly onDidPreferencesChanged = this.onDidPreferencesChangedEmitter.event;
440
441
async get<T>(preferenceName: string): Promise<T | undefined> {
442
// Load from database
443
return this.database.getPreference(preferenceName);
444
}
445
446
async set(preferenceName: string, value: any): Promise<boolean> {
447
const oldValue = await this.get(preferenceName);
448
await this.database.setPreference(preferenceName, value);
449
450
// Fire change event
451
this.onDidPreferencesChangedEmitter.fire({
452
[preferenceName]: {
453
newValue: value,
454
oldValue,
455
scope: PreferenceScope.User
456
}
457
});
458
459
return true;
460
}
461
462
getPreferenceNames(): string[] {
463
return this.database.getAllPreferenceNames();
464
}
465
}
466
```
467
468
## Types
469
470
```typescript { .api }
471
/**
472
* Preference-related type definitions
473
*/
474
type PreferenceValue = string | number | boolean | object | undefined;
475
476
type PreferenceInspection<T> = {
477
preferenceName: string;
478
defaultValue?: T;
479
globalValue?: T;
480
workspaceValue?: T;
481
folderValue?: T;
482
value?: T;
483
};
484
485
interface PreferenceConfiguration {
486
[name: string]: PreferenceValue;
487
}
488
```