Core framework for building cloud and desktop IDE applications using modern web technologies with TypeScript and dependency injection.
—
Theia's command system provides centralized command registry with handler prioritization, context awareness, and event-driven lifecycle management for building extensible applications.
Define commands that can be executed throughout the application.
/**
* Command definition interface
*/
interface Command {
/** Unique command identifier */
id: string;
/** Human-readable label for UI display */
label?: string;
/** CSS class for command icon */
iconClass?: string;
/** Short title used for display in menus */
shortTitle?: string;
/** Category for grouping commands */
category?: string;
/** Original label before localization */
originalLabel?: string;
/** Original category before localization */
originalCategory?: string;
}
/**
* Command utility functions
*/
namespace Command {
/**
* Determine whether object is a Command
* @param arg - Object to test
* @returns True if object is a Command
*/
function is(arg: unknown): arg is Command;
/**
* Utility function to easily translate commands
* @param command - Command to localize
* @param nlsLabelKey - NLS key for label (defaults to command.id)
* @param nlsCategoryKey - NLS key for category
* @returns Localized command
*/
function toLocalizedCommand(command: Command, nlsLabelKey?: string, nlsCategoryKey?: string): Command;
/**
* Convert command to default localized version
* @param command - Command to localize
* @returns Default localized command
*/
function toDefaultLocalizedCommand(command: Command): Command;
/**
* Comparator function for sorting commands
* @param a - First command
* @param b - Second command
* @returns Comparison result
*/
function compareCommands(a: Command, b: Command): number;
/**
* Determine if two commands are equal
* @param a - First command for comparison
* @param b - Second command for comparison
* @returns True if commands are equal
*/
function equals(a: Command, b: Command): boolean;
}Usage Example:
import { Command } from "@theia/core";
export const SAVE_COMMAND: Command = {
id: 'core.save',
label: 'Save',
iconClass: 'fa fa-save',
category: 'File'
};
export const CUSTOM_COMMAND: Command = {
id: 'my-extension.custom-action',
label: 'Custom Action',
category: 'Custom'
};Implement command execution logic with optional enablement and visibility conditions.
/**
* Command handler interface
*/
interface CommandHandler {
/**
* Execute the command with optional arguments
* @param args - Command arguments
* @returns Command result or promise
*/
execute(...args: any[]): any;
/**
* Determine if command is enabled in current context
* @param args - Command arguments
* @returns True if command should be enabled
*/
isEnabled?(...args: any[]): boolean;
/**
* Determine if command is visible in current context
* @param args - Command arguments
* @returns True if command should be visible
*/
isVisible?(...args: any[]): boolean;
/**
* Determine if command should toggle state
* @param args - Command arguments
* @returns True if command is in toggled state
*/
isToggled?(...args: any[]): boolean;
}Usage Examples:
import { CommandHandler } from "@theia/core";
// Simple command handler
const saveHandler: CommandHandler = {
execute: () => {
console.log("Save executed");
return "saved";
}
};
// Context-aware command handler
const conditionalHandler: CommandHandler = {
execute: (uri: string) => {
console.log(`Processing: ${uri}`);
},
isEnabled: (uri: string) => {
return uri && uri.endsWith('.txt');
},
isVisible: (uri: string) => {
return uri !== undefined;
}
};Central registry for registering commands and handlers with execution capabilities.
/**
* Command registry interface
*/
interface CommandRegistry {
/**
* Register a command with optional handler
* @param command - Command definition
* @param handler - Optional command handler
* @returns Disposable to unregister the command
*/
registerCommand(command: Command, handler?: CommandHandler): Disposable;
/**
* Unregister command from the registry
* @param command - Command to unregister
*/
unregisterCommand(command: Command): void;
/**
* Unregister command from the registry
* @param id - Command ID to unregister
*/
unregisterCommand(id: string): void;
/**
* Register a handler for a command ID
* @param commandId - Command ID
* @param handler - Command handler
* @returns Disposable to unregister the handler
*/
registerHandler(commandId: string, handler: CommandHandler): Disposable;
/**
* Execute a command by ID with arguments
* @param id - Command ID
* @param args - Command arguments
* @returns Promise resolving to command result
*/
executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
/**
* Get all registered commands with their handlers
* @returns Iterator of commands with handlers
*/
getAllCommands(): IterableIterator<Readonly<Command & { handlers: CommandHandler[] }>>;
/**
* Get all handlers for a command
* @param commandId - Command ID
* @returns Array of handlers for the command
*/
getAllHandlers(commandId: string): CommandHandler[];
/**
* Get command by ID
* @param id - Command ID
* @returns Command definition or undefined
*/
getCommand(id: string): Command | undefined;
/**
* Get all registered command objects
* @returns Array of all registered commands
*/
readonly commands: Command[];
/**
* Get all registered command IDs
* @returns Array of all command IDs
*/
readonly commandIds: string[];
/**
* Get visible handler for command
* @param commandId - Command ID
* @param args - Command arguments
* @returns Visible handler or undefined
*/
getVisibleHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
/**
* Get active handler for command
* @param commandId - Command ID
* @param args - Command arguments
* @returns Active handler or undefined
*/
getActiveHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
/**
* Get toggled handler for command
* @param commandId - Command ID
* @param args - Command arguments
* @returns Toggled handler or undefined
*/
getToggledHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
/**
* Check if command is enabled
* @param id - Command ID
* @param args - Command arguments
* @returns True if command is enabled
*/
isEnabled(id: string, ...args: any[]): boolean;
/**
* Check if command is visible
* @param id - Command ID
* @param args - Command arguments
* @returns True if command is visible
*/
isVisible(id: string, ...args: any[]): boolean;
/**
* Check if command is toggled
* @param id - Command ID
* @param args - Command arguments
* @returns True if command is toggled
*/
isToggled(id: string, ...args: any[]): boolean;
/**
* Get the list of recently used commands
*/
recent: Command[];
/**
* Add a command to recently used list
* @param recent - Recent command or array of commands
*/
addRecentCommand(recent: Command | Command[]): void;
/**
* Clear the list of recently used commands
*/
clearCommandHistory(): void;
/**
* Event fired when commands change
*/
readonly onCommandsChanged: Event<void>;
/**
* Event fired before command execution
*/
readonly onWillExecuteCommand: Event<WillExecuteCommandEvent>;
/**
* Event fired after command execution
*/
readonly onDidExecuteCommand: Event<CommandEvent>;
}
/**
* Command event types
*/
interface CommandEvent {
commandId: string;
args: any[];
}
interface WillExecuteCommandEvent {
commandId: string;
args: any[];
waitUntil(thenable: Promise<any>): void;
}
/**
* Service token for CommandRegistry
*/
const CommandRegistry: symbol;Usage Example:
import { inject, injectable } from "@theia/core";
import { CommandRegistry, Command, CommandHandler } from "@theia/core";
@injectable()
export class MyCommandManager {
constructor(
@inject(CommandRegistry) private readonly commands: CommandRegistry
) {}
registerCommands(): void {
// Register command with handler
this.commands.registerCommand(SAVE_COMMAND, {
execute: () => this.save()
});
// Register command without handler (handler added later)
this.commands.registerCommand(CUSTOM_COMMAND);
}
async executeCustomCommand(): Promise<void> {
const result = await this.commands.executeCommand('my-extension.custom-action');
console.log('Command result:', result);
}
private save(): void {
console.log("Saving...");
}
}High-level service interface for command execution.
/**
* Command service interface
*/
interface CommandService {
/**
* Execute a command by ID
* @param id - Command ID
* @param args - Command arguments
* @returns Promise resolving to command result
*/
executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
}
/**
* Service token for CommandService
*/
const CommandService: symbol;Extension point for contributing commands to the application.
/**
* Command contribution interface for extensions
*/
interface CommandContribution {
/**
* Register commands with the command registry
* @param commands - Command registry instance
*/
registerCommands(commands: CommandRegistry): void;
}
/**
* Service token for CommandContribution
*/
const CommandContribution: symbol;Usage Example:
import { injectable } from "@theia/core";
import { CommandContribution, CommandRegistry } from "@theia/core";
@injectable()
export class MyCommandContribution implements CommandContribution {
registerCommands(registry: CommandRegistry): void {
registry.registerCommand({
id: 'my-extension.hello',
label: 'Say Hello',
category: 'Demo'
}, {
execute: () => {
console.log('Hello from my extension!');
return 'Hello executed';
},
isEnabled: () => true,
isVisible: () => true
});
registry.registerCommand({
id: 'my-extension.toggle',
label: 'Toggle Feature',
category: 'Demo'
}, {
execute: () => {
this.toggleFeature();
},
isToggled: () => this.isFeatureEnabled()
});
}
private toggleFeature(): void {
// Toggle implementation
}
private isFeatureEnabled(): boolean {
// Return current state
return false;
}
}Events related to command execution lifecycle.
/**
* Command event fired when command is executed
*/
interface CommandEvent {
/** Command ID that was executed */
commandId: string;
/** Arguments passed to command */
args: any[];
}
/**
* Event fired before command execution
*/
interface WillExecuteCommandEvent {
/** Command ID about to be executed */
commandId: string;
/** Arguments to be passed to command */
args: any[];
}Register multiple handlers for the same command with priority:
// Higher priority handler (registered later wins)
registry.registerCommand(SAVE_COMMAND, primarySaveHandler);
registry.registerCommand(SAVE_COMMAND, fallbackSaveHandler);Create commands that adapt to context:
const CONTEXT_COMMAND: Command = {
id: 'context.action',
label: 'Context Action'
};
const contextHandler: CommandHandler = {
execute: (context: any) => {
if (context.type === 'file') {
return this.handleFile(context);
} else if (context.type === 'folder') {
return this.handleFolder(context);
}
},
isEnabled: (context: any) => {
return context && (context.type === 'file' || context.type === 'folder');
},
isVisible: (context: any) => {
return context !== undefined;
}
};Execute multiple commands in sequence:
async executeCommandChain(): Promise<void> {
await this.commands.executeCommand('prepare.action');
const result = await this.commands.executeCommand('main.action', result);
await this.commands.executeCommand('cleanup.action', result);
}/**
* Command utilities namespace
*/
namespace Command {
/**
* Type predicate to check if object is a Command
* @param arg - Object to check
* @returns True if object is a Command
*/
function is(arg: any): arg is Command;
/**
* Compare two commands for equality
* @param a - First command
* @param b - Second command
* @returns True if commands are equal
*/
function equals(a: Command, b: Command): boolean;
}
/**
* Disposable interface for cleaning up registrations
*/
interface Disposable {
dispose(): void;
}Install with Tessl CLI
npx tessl i tessl/npm-theia--core