0
# Command System
1
2
Theia's command system provides centralized command registry with handler prioritization, context awareness, and event-driven lifecycle management for building extensible applications.
3
4
## Capabilities
5
6
### Command Definition
7
8
Define commands that can be executed throughout the application.
9
10
```typescript { .api }
11
/**
12
* Command definition interface
13
*/
14
interface Command {
15
/** Unique command identifier */
16
id: string;
17
/** Human-readable label for UI display */
18
label?: string;
19
/** CSS class for command icon */
20
iconClass?: string;
21
/** Short title used for display in menus */
22
shortTitle?: string;
23
/** Category for grouping commands */
24
category?: string;
25
/** Original label before localization */
26
originalLabel?: string;
27
/** Original category before localization */
28
originalCategory?: string;
29
}
30
31
/**
32
* Command utility functions
33
*/
34
namespace Command {
35
/**
36
* Determine whether object is a Command
37
* @param arg - Object to test
38
* @returns True if object is a Command
39
*/
40
function is(arg: unknown): arg is Command;
41
42
/**
43
* Utility function to easily translate commands
44
* @param command - Command to localize
45
* @param nlsLabelKey - NLS key for label (defaults to command.id)
46
* @param nlsCategoryKey - NLS key for category
47
* @returns Localized command
48
*/
49
function toLocalizedCommand(command: Command, nlsLabelKey?: string, nlsCategoryKey?: string): Command;
50
51
/**
52
* Convert command to default localized version
53
* @param command - Command to localize
54
* @returns Default localized command
55
*/
56
function toDefaultLocalizedCommand(command: Command): Command;
57
58
/**
59
* Comparator function for sorting commands
60
* @param a - First command
61
* @param b - Second command
62
* @returns Comparison result
63
*/
64
function compareCommands(a: Command, b: Command): number;
65
66
/**
67
* Determine if two commands are equal
68
* @param a - First command for comparison
69
* @param b - Second command for comparison
70
* @returns True if commands are equal
71
*/
72
function equals(a: Command, b: Command): boolean;
73
}
74
```
75
76
**Usage Example:**
77
78
```typescript
79
import { Command } from "@theia/core";
80
81
export const SAVE_COMMAND: Command = {
82
id: 'core.save',
83
label: 'Save',
84
iconClass: 'fa fa-save',
85
category: 'File'
86
};
87
88
export const CUSTOM_COMMAND: Command = {
89
id: 'my-extension.custom-action',
90
label: 'Custom Action',
91
category: 'Custom'
92
};
93
```
94
95
### Command Handler
96
97
Implement command execution logic with optional enablement and visibility conditions.
98
99
```typescript { .api }
100
/**
101
* Command handler interface
102
*/
103
interface CommandHandler {
104
/**
105
* Execute the command with optional arguments
106
* @param args - Command arguments
107
* @returns Command result or promise
108
*/
109
execute(...args: any[]): any;
110
111
/**
112
* Determine if command is enabled in current context
113
* @param args - Command arguments
114
* @returns True if command should be enabled
115
*/
116
isEnabled?(...args: any[]): boolean;
117
118
/**
119
* Determine if command is visible in current context
120
* @param args - Command arguments
121
* @returns True if command should be visible
122
*/
123
isVisible?(...args: any[]): boolean;
124
125
/**
126
* Determine if command should toggle state
127
* @param args - Command arguments
128
* @returns True if command is in toggled state
129
*/
130
isToggled?(...args: any[]): boolean;
131
}
132
```
133
134
**Usage Examples:**
135
136
```typescript
137
import { CommandHandler } from "@theia/core";
138
139
// Simple command handler
140
const saveHandler: CommandHandler = {
141
execute: () => {
142
console.log("Save executed");
143
return "saved";
144
}
145
};
146
147
// Context-aware command handler
148
const conditionalHandler: CommandHandler = {
149
execute: (uri: string) => {
150
console.log(`Processing: ${uri}`);
151
},
152
153
isEnabled: (uri: string) => {
154
return uri && uri.endsWith('.txt');
155
},
156
157
isVisible: (uri: string) => {
158
return uri !== undefined;
159
}
160
};
161
```
162
163
### Command Registry
164
165
Central registry for registering commands and handlers with execution capabilities.
166
167
```typescript { .api }
168
/**
169
* Command registry interface
170
*/
171
interface CommandRegistry {
172
/**
173
* Register a command with optional handler
174
* @param command - Command definition
175
* @param handler - Optional command handler
176
* @returns Disposable to unregister the command
177
*/
178
registerCommand(command: Command, handler?: CommandHandler): Disposable;
179
180
/**
181
* Unregister command from the registry
182
* @param command - Command to unregister
183
*/
184
unregisterCommand(command: Command): void;
185
186
/**
187
* Unregister command from the registry
188
* @param id - Command ID to unregister
189
*/
190
unregisterCommand(id: string): void;
191
192
/**
193
* Register a handler for a command ID
194
* @param commandId - Command ID
195
* @param handler - Command handler
196
* @returns Disposable to unregister the handler
197
*/
198
registerHandler(commandId: string, handler: CommandHandler): Disposable;
199
200
/**
201
* Execute a command by ID with arguments
202
* @param id - Command ID
203
* @param args - Command arguments
204
* @returns Promise resolving to command result
205
*/
206
executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
207
208
/**
209
* Get all registered commands with their handlers
210
* @returns Iterator of commands with handlers
211
*/
212
getAllCommands(): IterableIterator<Readonly<Command & { handlers: CommandHandler[] }>>;
213
214
/**
215
* Get all handlers for a command
216
* @param commandId - Command ID
217
* @returns Array of handlers for the command
218
*/
219
getAllHandlers(commandId: string): CommandHandler[];
220
221
/**
222
* Get command by ID
223
* @param id - Command ID
224
* @returns Command definition or undefined
225
*/
226
getCommand(id: string): Command | undefined;
227
228
/**
229
* Get all registered command objects
230
* @returns Array of all registered commands
231
*/
232
readonly commands: Command[];
233
234
/**
235
* Get all registered command IDs
236
* @returns Array of all command IDs
237
*/
238
readonly commandIds: string[];
239
240
/**
241
* Get visible handler for command
242
* @param commandId - Command ID
243
* @param args - Command arguments
244
* @returns Visible handler or undefined
245
*/
246
getVisibleHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
247
248
/**
249
* Get active handler for command
250
* @param commandId - Command ID
251
* @param args - Command arguments
252
* @returns Active handler or undefined
253
*/
254
getActiveHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
255
256
/**
257
* Get toggled handler for command
258
* @param commandId - Command ID
259
* @param args - Command arguments
260
* @returns Toggled handler or undefined
261
*/
262
getToggledHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
263
264
/**
265
* Check if command is enabled
266
* @param id - Command ID
267
* @param args - Command arguments
268
* @returns True if command is enabled
269
*/
270
isEnabled(id: string, ...args: any[]): boolean;
271
272
/**
273
* Check if command is visible
274
* @param id - Command ID
275
* @param args - Command arguments
276
* @returns True if command is visible
277
*/
278
isVisible(id: string, ...args: any[]): boolean;
279
280
/**
281
* Check if command is toggled
282
* @param id - Command ID
283
* @param args - Command arguments
284
* @returns True if command is toggled
285
*/
286
isToggled(id: string, ...args: any[]): boolean;
287
288
/**
289
* Get the list of recently used commands
290
*/
291
recent: Command[];
292
293
/**
294
* Add a command to recently used list
295
* @param recent - Recent command or array of commands
296
*/
297
addRecentCommand(recent: Command | Command[]): void;
298
299
/**
300
* Clear the list of recently used commands
301
*/
302
clearCommandHistory(): void;
303
304
/**
305
* Event fired when commands change
306
*/
307
readonly onCommandsChanged: Event<void>;
308
309
/**
310
* Event fired before command execution
311
*/
312
readonly onWillExecuteCommand: Event<WillExecuteCommandEvent>;
313
314
/**
315
* Event fired after command execution
316
*/
317
readonly onDidExecuteCommand: Event<CommandEvent>;
318
}
319
320
/**
321
* Command event types
322
*/
323
interface CommandEvent {
324
commandId: string;
325
args: any[];
326
}
327
328
interface WillExecuteCommandEvent {
329
commandId: string;
330
args: any[];
331
waitUntil(thenable: Promise<any>): void;
332
}
333
334
/**
335
* Service token for CommandRegistry
336
*/
337
const CommandRegistry: symbol;
338
```
339
340
**Usage Example:**
341
342
```typescript
343
import { inject, injectable } from "@theia/core";
344
import { CommandRegistry, Command, CommandHandler } from "@theia/core";
345
346
@injectable()
347
export class MyCommandManager {
348
constructor(
349
@inject(CommandRegistry) private readonly commands: CommandRegistry
350
) {}
351
352
registerCommands(): void {
353
// Register command with handler
354
this.commands.registerCommand(SAVE_COMMAND, {
355
execute: () => this.save()
356
});
357
358
// Register command without handler (handler added later)
359
this.commands.registerCommand(CUSTOM_COMMAND);
360
}
361
362
async executeCustomCommand(): Promise<void> {
363
const result = await this.commands.executeCommand('my-extension.custom-action');
364
console.log('Command result:', result);
365
}
366
367
private save(): void {
368
console.log("Saving...");
369
}
370
}
371
```
372
373
### Command Service
374
375
High-level service interface for command execution.
376
377
```typescript { .api }
378
/**
379
* Command service interface
380
*/
381
interface CommandService {
382
/**
383
* Execute a command by ID
384
* @param id - Command ID
385
* @param args - Command arguments
386
* @returns Promise resolving to command result
387
*/
388
executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
389
}
390
391
/**
392
* Service token for CommandService
393
*/
394
const CommandService: symbol;
395
```
396
397
### Command Contribution
398
399
Extension point for contributing commands to the application.
400
401
```typescript { .api }
402
/**
403
* Command contribution interface for extensions
404
*/
405
interface CommandContribution {
406
/**
407
* Register commands with the command registry
408
* @param commands - Command registry instance
409
*/
410
registerCommands(commands: CommandRegistry): void;
411
}
412
413
/**
414
* Service token for CommandContribution
415
*/
416
const CommandContribution: symbol;
417
```
418
419
**Usage Example:**
420
421
```typescript
422
import { injectable } from "@theia/core";
423
import { CommandContribution, CommandRegistry } from "@theia/core";
424
425
@injectable()
426
export class MyCommandContribution implements CommandContribution {
427
428
registerCommands(registry: CommandRegistry): void {
429
registry.registerCommand({
430
id: 'my-extension.hello',
431
label: 'Say Hello',
432
category: 'Demo'
433
}, {
434
execute: () => {
435
console.log('Hello from my extension!');
436
return 'Hello executed';
437
},
438
isEnabled: () => true,
439
isVisible: () => true
440
});
441
442
registry.registerCommand({
443
id: 'my-extension.toggle',
444
label: 'Toggle Feature',
445
category: 'Demo'
446
}, {
447
execute: () => {
448
this.toggleFeature();
449
},
450
isToggled: () => this.isFeatureEnabled()
451
});
452
}
453
454
private toggleFeature(): void {
455
// Toggle implementation
456
}
457
458
private isFeatureEnabled(): boolean {
459
// Return current state
460
return false;
461
}
462
}
463
```
464
465
### Command Events
466
467
Events related to command execution lifecycle.
468
469
```typescript { .api }
470
/**
471
* Command event fired when command is executed
472
*/
473
interface CommandEvent {
474
/** Command ID that was executed */
475
commandId: string;
476
/** Arguments passed to command */
477
args: any[];
478
}
479
480
/**
481
* Event fired before command execution
482
*/
483
interface WillExecuteCommandEvent {
484
/** Command ID about to be executed */
485
commandId: string;
486
/** Arguments to be passed to command */
487
args: any[];
488
}
489
```
490
491
## Advanced Usage Patterns
492
493
### Handler Prioritization
494
495
Register multiple handlers for the same command with priority:
496
497
```typescript
498
// Higher priority handler (registered later wins)
499
registry.registerCommand(SAVE_COMMAND, primarySaveHandler);
500
registry.registerCommand(SAVE_COMMAND, fallbackSaveHandler);
501
```
502
503
### Conditional Commands
504
505
Create commands that adapt to context:
506
507
```typescript
508
const CONTEXT_COMMAND: Command = {
509
id: 'context.action',
510
label: 'Context Action'
511
};
512
513
const contextHandler: CommandHandler = {
514
execute: (context: any) => {
515
if (context.type === 'file') {
516
return this.handleFile(context);
517
} else if (context.type === 'folder') {
518
return this.handleFolder(context);
519
}
520
},
521
522
isEnabled: (context: any) => {
523
return context && (context.type === 'file' || context.type === 'folder');
524
},
525
526
isVisible: (context: any) => {
527
return context !== undefined;
528
}
529
};
530
```
531
532
### Command Chaining
533
534
Execute multiple commands in sequence:
535
536
```typescript
537
async executeCommandChain(): Promise<void> {
538
await this.commands.executeCommand('prepare.action');
539
const result = await this.commands.executeCommand('main.action', result);
540
await this.commands.executeCommand('cleanup.action', result);
541
}
542
```
543
544
## Types
545
546
```typescript { .api }
547
/**
548
* Command utilities namespace
549
*/
550
namespace Command {
551
/**
552
* Type predicate to check if object is a Command
553
* @param arg - Object to check
554
* @returns True if object is a Command
555
*/
556
function is(arg: any): arg is Command;
557
558
/**
559
* Compare two commands for equality
560
* @param a - First command
561
* @param b - Second command
562
* @returns True if commands are equal
563
*/
564
function equals(a: Command, b: Command): boolean;
565
}
566
567
/**
568
* Disposable interface for cleaning up registrations
569
*/
570
interface Disposable {
571
dispose(): void;
572
}
573
```