Core framework for building cloud and desktop IDE applications using modern web technologies with TypeScript and dependency injection.
—
Theia's preference system provides type-safe preference management with schema definition, multiple providers, and runtime configuration changes for customizable applications.
Core service for accessing and modifying application preferences.
/**
* Service for managing application preferences
*/
interface PreferenceService {
/**
* Get preference value
* @param preferenceName - Preference identifier
* @param defaultValue - Default value if preference not set
* @returns Preference value or default
*/
get<T>(preferenceName: string, defaultValue?: T): T | undefined;
/**
* Set preference value
* @param preferenceName - Preference identifier
* @param value - Value to set
* @returns Promise that resolves when preference is saved
*/
set(preferenceName: string, value: any): Promise<void>;
/**
* Check if preference has been set
* @param preferenceName - Preference identifier
* @returns True if preference exists
*/
has(preferenceName: string): boolean;
/**
* Remove preference
* @param preferenceName - Preference identifier
* @returns Promise that resolves when preference is removed
*/
unset(preferenceName: string): Promise<void>;
/**
* Event fired when preferences change
*/
readonly onPreferenceChanged: Event<PreferenceChangeEvent<any>>;
/**
* Event fired when preferences are about to change
*/
readonly onPreferencesChanged: Event<PreferenceChanges>;
}
/**
* Service token for PreferenceService
*/
const PreferenceService: symbol;Usage Example:
import { inject, injectable } from "@theia/core";
import { PreferenceService } from "@theia/core/lib/browser";
@injectable()
export class MyConfigurableService {
constructor(
@inject(PreferenceService) private readonly preferences: PreferenceService
) {
// Listen for preference changes
this.preferences.onPreferenceChanged(event => {
if (event.preferenceName === 'myExtension.enableFeature') {
this.updateFeatureState(event.newValue);
}
});
}
private updateFeatureState(enabled: boolean): void {
console.log(`Feature ${enabled ? 'enabled' : 'disabled'}`);
}
async configure(): Promise<void> {
// Get preference with default
const theme = this.preferences.get('workbench.colorTheme', 'dark');
// Check if preference exists
if (!this.preferences.has('myExtension.initialized')) {
await this.preferences.set('myExtension.initialized', true);
}
// Get typed preference
const maxFiles = this.preferences.get<number>('files.maxOpenFiles', 50);
}
}Define preference schemas with validation, types, and UI metadata.
/**
* Preference schema definition
*/
interface PreferenceSchema {
[name: string]: PreferenceSchemaProperty;
}
/**
* Individual preference property definition
*/
interface PreferenceSchemaProperty {
/** Property type */
type?: 'boolean' | 'string' | 'number' | 'integer' | 'array' | 'object';
/** Default value */
default?: any;
/** Human-readable description */
description?: string;
/** Allowed enum values */
enum?: any[];
/** Enum descriptions */
enumDescriptions?: string[];
/** Minimum value (numbers) */
minimum?: number;
/** Maximum value (numbers) */
maximum?: number;
/** Array item type */
items?: PreferenceSchemaProperty;
/** Object property definitions */
properties?: { [key: string]: PreferenceSchemaProperty };
/** Additional properties allowed */
additionalProperties?: boolean | PreferenceSchemaProperty;
/** Preference scope */
scope?: PreferenceScope;
/** UI order hint */
order?: number;
/** Deprecation message */
deprecationMessage?: string;
}
/**
* Preference scope enumeration
*/
enum PreferenceScope {
/** User-wide preferences */
User = 1,
/** Workspace-specific preferences */
Workspace = 2,
/** Folder-specific preferences */
Folder = 3
}Usage Example:
import { PreferenceSchema } from "@theia/core/lib/browser";
export const myExtensionPreferenceSchema: PreferenceSchema = {
'myExtension.enableFeature': {
type: 'boolean',
default: true,
description: 'Enable the special feature',
scope: PreferenceScope.User
},
'myExtension.maxRetries': {
type: 'integer',
default: 3,
minimum: 1,
maximum: 10,
description: 'Maximum number of retry attempts'
},
'myExtension.theme': {
type: 'string',
default: 'auto',
enum: ['light', 'dark', 'auto'],
enumDescriptions: [
'Light theme',
'Dark theme',
'Auto-detect from system'
],
description: 'Color theme preference'
},
'myExtension.excludePatterns': {
type: 'array',
items: {
type: 'string'
},
default: ['*.tmp', '*.log'],
description: 'File patterns to exclude'
}
};Type-safe preference access with automatic change detection.
/**
* Type-safe preference proxy
*/
class PreferenceProxy<T> {
/**
* Event fired when preferences change
*/
readonly onPreferenceChanged: Event<PreferenceChangeEvent<T>>;
/**
* Get all preference values
* @returns Current preference values
*/
getPreferences(): T;
/**
* Access preferences like object properties
*/
[K in keyof T]: T[K];
}
/**
* Create preference proxy
* @param preferences - Preference service
* @param schema - Preference schema
* @returns Type-safe preference proxy
*/
function createPreferenceProxy<T>(
preferences: PreferenceService,
schema: PreferenceSchema
): PreferenceProxy<T>;Usage Example:
interface MyExtensionConfig {
enableFeature: boolean;
maxRetries: number;
theme: 'light' | 'dark' | 'auto';
excludePatterns: string[];
}
@injectable()
export class MyConfigurableService {
private readonly config: PreferenceProxy<MyExtensionConfig>;
constructor(
@inject(PreferenceService) preferences: PreferenceService
) {
this.config = createPreferenceProxy(preferences, myExtensionPreferenceSchema);
// Listen for specific preference changes
this.config.onPreferenceChanged(event => {
if (event.preferenceName === 'myExtension.enableFeature') {
this.handleFeatureToggle(event.newValue);
}
});
}
private handleFeatureToggle(enabled: boolean): void {
// Type-safe access to preferences
console.log(`Feature ${enabled ? 'enabled' : 'disabled'}`);
console.log(`Max retries: ${this.config.maxRetries}`);
console.log(`Theme: ${this.config.theme}`);
console.log(`Exclude patterns: ${this.config.excludePatterns.join(', ')}`);
}
}Extensible preference provider system for different preference sources.
/**
* Preference provider interface
*/
interface PreferenceProvider {
/**
* Get preference value
* @param preferenceName - Preference name
* @returns Preference value or undefined
*/
get<T>(preferenceName: string): T | undefined;
/**
* Set preference value
* @param preferenceName - Preference name
* @param value - Value to set
* @returns Promise that resolves when saved
*/
set(preferenceName: string, value: any): Promise<boolean>;
/**
* Get all preference names
* @returns Array of preference names
*/
getPreferenceNames(): string[];
/**
* Event fired when preferences change
*/
readonly onDidPreferencesChanged: Event<PreferenceChanges>;
/**
* Provider domain (user, workspace, folder)
*/
readonly domain: PreferenceDomain;
}
/**
* Preference domain types
*/
type PreferenceDomain = 'user' | 'workspace' | 'folder';
/**
* Service token for PreferenceProvider
*/
const PreferenceProvider: symbol;Support for various configuration file formats.
/**
* JSON preference provider for .json config files
*/
class JsonPreferenceProvider implements PreferenceProvider {
constructor(uri: URI, scope: PreferenceScope);
get<T>(preferenceName: string): T | undefined;
set(preferenceName: string, value: any): Promise<boolean>;
getPreferenceNames(): string[];
readonly onDidPreferencesChanged: Event<PreferenceChanges>;
readonly domain: PreferenceDomain;
}
/**
* Workspace preference provider
*/
class WorkspacePreferenceProvider extends JsonPreferenceProvider {
constructor(workspaceUri: URI);
}
/**
* User preference provider
*/
class UserPreferenceProvider extends JsonPreferenceProvider {
constructor();
}Detailed information about preference changes.
/**
* Event fired when a preference changes
*/
interface PreferenceChangeEvent<T> {
/** Name of the preference that changed */
readonly preferenceName: string;
/** New preference value */
readonly newValue: T | undefined;
/** Previous preference value */
readonly oldValue: T | undefined;
/** Preference scope that changed */
readonly scope: PreferenceScope;
/** Domain where change occurred */
readonly domain: PreferenceDomain;
}
/**
* Collection of preference changes
*/
interface PreferenceChanges {
[preferenceName: string]: PreferenceChange;
}
/**
* Individual preference change
*/
interface PreferenceChange {
/** New value */
readonly newValue: any;
/** Old value */
readonly oldValue: any;
/** Change scope */
readonly scope: PreferenceScope;
}Create custom providers for specialized preference sources.
import { injectable } from "@theia/core";
import { PreferenceProvider, PreferenceChanges } from "@theia/core/lib/browser";
@injectable()
export class DatabasePreferenceProvider implements PreferenceProvider {
readonly domain = 'user';
private readonly onDidPreferencesChangedEmitter = new Emitter<PreferenceChanges>();
readonly onDidPreferencesChanged = this.onDidPreferencesChangedEmitter.event;
async get<T>(preferenceName: string): Promise<T | undefined> {
// Load from database
return this.database.getPreference(preferenceName);
}
async set(preferenceName: string, value: any): Promise<boolean> {
const oldValue = await this.get(preferenceName);
await this.database.setPreference(preferenceName, value);
// Fire change event
this.onDidPreferencesChangedEmitter.fire({
[preferenceName]: {
newValue: value,
oldValue,
scope: PreferenceScope.User
}
});
return true;
}
getPreferenceNames(): string[] {
return this.database.getAllPreferenceNames();
}
}/**
* Preference-related type definitions
*/
type PreferenceValue = string | number | boolean | object | undefined;
type PreferenceInspection<T> = {
preferenceName: string;
defaultValue?: T;
globalValue?: T;
workspaceValue?: T;
folderValue?: T;
value?: T;
};
interface PreferenceConfiguration {
[name: string]: PreferenceValue;
}Install with Tessl CLI
npx tessl i tessl/npm-theia--core