1
>
/*---------------------------------------------------------------------------------------------
commands.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { Emitter, Event } from '../../../base/common/event.js';
7
>
import { Iterable } from '../../../base/common/iterator.js';
8
>
import { IJSONSchema } from '../../../base/common/jsonSchema.js';
9
>
import { IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js';
10
>
import { LinkedList } from '../../../base/common/linkedList.js';
11
>
import { TypeConstraint, validateConstraints } from '../../../base/common/types.js';
12
>
import { ILocalizedString } from '../../action/common/action.js';
13
>
import { createDecorator, ServicesAccessor } from '../../instantiation/common/instantiation.js';
14
>
15
>
export const ICommandService = createDecorator<ICommandService>('commandService');
16
>
17
>
export interface ICommandEvent {
18
>
readonly commandId: string;
19
>
readonly args: unknown[];
20
>
}
21
>
22
>
export interface ICommandService {
23
>
readonly _serviceBrand: undefined;
24
>
readonly onWillExecuteCommand: Event<ICommandEvent>;
25
>
readonly onDidExecuteCommand: Event<ICommandEvent>;
26
>
executeCommand<R = unknown>(commandId: string, ...args: unknown[]): Promise<R | undefined>;
27
>
}
28
>
29
>
export type ICommandsMap = Map<string, ICommand>;
30
>
31
>
export type ICommandHandler<Args extends unknown[] = unknown[], R = void> = (accessor: ServicesAccessor, ...args: Args) => R;
32
>
33
>
export interface ICommand<Args extends unknown[] = unknown[], R = void> {
34
>
id: string;
35
>
handler: ICommandHandler<Args, R>;
36
>
metadata?: ICommandMetadata | null;
37
>
}
38
>
39
>
export interface ICommandMetadata {
40
>
/**
41
>
* NOTE: Please use an ILocalizedString. string is in the type for backcompat for now.
42
>
* A short summary of what the command does. This will be used in:
43
>
* - API commands
44
>
* - when showing keybindings that have no other UX
45
>
* - when searching for commands in the Command Palette
46
>
*/
47
>
readonly description: ILocalizedString | string;
48
>
readonly args?: ReadonlyArray<{
49
>
readonly name: string;
50
>
readonly isOptional?: boolean;
51
>
readonly description?: string;
52
>
readonly constraint?: TypeConstraint;
53
>
readonly schema?: IJSONSchema;
54
>
}>;
55
>
readonly returns?: string;
56
>
}
57
>
58
>
export interface ICommandRegistry {
59
>
readonly onDidRegisterCommand: Event<string>;
60
>
registerCommand<Args extends unknown[]>(id: string, command: ICommandHandler<Args>): IDisposable;
61
>
registerCommand<Args extends unknown[]>(command: ICommand<Args>): IDisposable;
62
>
registerCommandAlias(oldId: string, newId: string): IDisposable;
63
>
getCommand(id: string): ICommand | undefined;
64
>
getCommands(): ICommandsMap;
65
>
}
66
>
67
>
export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry {
68
>
69
>
private readonly _commands = new Map<string, LinkedList<ICommand>>();
70
>
71
>
private readonly _onDidRegisterCommand = new Emitter<string>();
72
>
readonly onDidRegisterCommand: Event<string> = this._onDidRegisterCommand.event;
73
>
74
>
registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable {
75
>
76
>
if (!idOrCommand) {
77
throw new Error(`invalid command`);
78
}
80
>
if (typeof idOrCommand === 'string') {
81
>
if (!handler) {
82
throw new Error(`invalid command`);
83
}
84
>
return this.registerCommand({ id: idOrCommand, handler });
commands.ts
85
>
}
86
>
87
>
// add argument validation if rich command metadata is provided
88
>
if (idOrCommand.metadata && Array.isArray(idOrCommand.metadata.args)) {
89
const constraints: Array<TypeConstraint | undefined> = [];
90
for (const arg of idOrCommand.metadata.args) {