Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/commands/common/commandService.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
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 { CancelablePromise, notCancellablePromise, raceCancellablePromises, timeout } from '../../../../base/common/async.js';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { Disposable } from '../../../../base/common/lifecycle.js';
9
import { CommandsRegistry, ICommandEvent, ICommandService } from '../../../../platform/commands/common/commands.js';
10
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
11
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
12
import { ILogService } from '../../../../platform/log/common/log.js';
13
import { IExtensionService } from '../../extensions/common/extensions.js';
14
15
export class CommandService extends Disposable implements ICommandService {
16
17
declare readonly _serviceBrand: undefined;
18
19
private _extensionHostIsReady: boolean = false;
20
private _starActivation: CancelablePromise<void> | null;
21
22
private readonly _onWillExecuteCommand: Emitter<ICommandEvent> = this._register(new Emitter<ICommandEvent>());
23
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
24
25
private readonly _onDidExecuteCommand: Emitter<ICommandEvent> = new Emitter<ICommandEvent>();
26
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;
27
28
constructor(
29
@IInstantiationService private readonly _instantiationService: IInstantiationService,
30
@IExtensionService private readonly _extensionService: IExtensionService,
31
@ILogService private readonly _logService: ILogService
32
) {
33
super();
34
this._extensionService.whenInstalledExtensionsRegistered().then(value => this._extensionHostIsReady = value);
35
this._starActivation = null;
36
}
37
38
private _activateStar(): Promise<void> {
39
if (!this._starActivation) {
40
// wait for * activation, limited to at most 30s.
41
this._starActivation = raceCancellablePromises([
42
this._extensionService.activateByEvent(`*`),
43
timeout(30000)
44
]);
45
}
46
47
// This is wrapped with notCancellablePromise so it doesn't get cancelled
48
// early because it is shared between consumers.
49
return notCancellablePromise(this._starActivation);
50
}
51
52
async executeCommand<T>(id: string, ...args: any[]): Promise<T> {
53
this._logService.trace('CommandService#executeCommand', id);
54
55
const activationEvent = `onCommand:${id}`;
56
const commandIsRegistered = !!CommandsRegistry.getCommand(id);
57
58
if (commandIsRegistered) {
59
60
// if the activation event has already resolved (i.e. subsequent call),
61
// we will execute the registered command immediately
62
if (this._extensionService.activationEventIsDone(activationEvent)) {
63
return this._tryExecuteCommand(id, args);
64
}
65
66
// if the extension host didn't start yet, we will execute the registered
67
// command immediately and send an activation event, but not wait for it
68
if (!this._extensionHostIsReady) {
69
this._extensionService.activateByEvent(activationEvent); // intentionally not awaited
70
return this._tryExecuteCommand(id, args);
71
}
72
73
// we will wait for a simple activation event (e.g. in case an extension wants to overwrite it)
74
await this._extensionService.activateByEvent(activationEvent);
75
return this._tryExecuteCommand(id, args);
76
}
77
78
// finally, if the command is not registered we will send a simple activation event
79
// as well as a * activation event raced against registration and against 30s
80
await Promise.all([
81
this._extensionService.activateByEvent(activationEvent),
82
raceCancellablePromises<unknown>([
83
// race * activation against command registration
84
this._activateStar(),
85
Event.toPromise(Event.filter(CommandsRegistry.onDidRegisterCommand, e => e === id))
86
]),
87
]);
88
89
return this._tryExecuteCommand(id, args);
90
}
91
92
private _tryExecuteCommand(id: string, args: any[]): Promise<any> {
93
const command = CommandsRegistry.getCommand(id);
94
if (!command) {
95
return Promise.reject(new Error(`command '${id}' not found`));
96
}
97
try {
98
this._onWillExecuteCommand.fire({ commandId: id, args });
99
const result = this._instantiationService.invokeFunction(command.handler, ...args);
100
this._onDidExecuteCommand.fire({ commandId: id, args });
101
return Promise.resolve(result);
102
} catch (err) {
103
return Promise.reject(err);
104
}
105
}
106
107
public override dispose(): void {
108
super.dispose();
109
this._starActivation?.cancel();
110
}
111
}
112
113
registerSingleton(ICommandService, CommandService, InstantiationType.Delayed);
114
115