Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/actions.ts
5240 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 { Emitter, Event } from './event.js';
7
import { Disposable, IDisposable } from './lifecycle.js';
8
import * as nls from '../../nls.js';
9
10
export interface ITelemetryData {
11
readonly from?: string;
12
readonly target?: string;
13
[key: string]: unknown;
14
}
15
16
export type WorkbenchActionExecutedClassification = {
17
id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was run.' };
18
from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the component the action was run from.' };
19
detail?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Optional details about how the action was run, e.g which keybinding was used.' };
20
owner: 'isidorn';
21
comment: 'Provides insight into actions that are executed within the workbench.';
22
};
23
24
export type WorkbenchActionExecutedEvent = {
25
id: string;
26
from: string;
27
detail?: string;
28
};
29
30
export interface IAction {
31
readonly id: string;
32
label: string;
33
tooltip: string;
34
class: string | undefined;
35
enabled: boolean;
36
checked?: boolean;
37
run(...args: unknown[]): unknown;
38
}
39
40
export interface IActionRunner extends IDisposable {
41
readonly onDidRun: Event<IRunEvent>;
42
readonly onWillRun: Event<IRunEvent>;
43
44
run(action: IAction, context?: unknown): unknown;
45
}
46
47
export interface IActionChangeEvent {
48
readonly label?: string;
49
readonly tooltip?: string;
50
readonly class?: string;
51
readonly enabled?: boolean;
52
readonly checked?: boolean;
53
}
54
55
/**
56
* A concrete implementation of {@link IAction}.
57
*
58
* Note that in most cases you should use the lighter-weight {@linkcode toAction} function instead.
59
*/
60
export class Action extends Disposable implements IAction {
61
62
protected _onDidChange = this._register(new Emitter<IActionChangeEvent>());
63
get onDidChange() { return this._onDidChange.event; }
64
65
protected readonly _id: string;
66
protected _label: string;
67
protected _tooltip: string | undefined;
68
protected _cssClass: string | undefined;
69
protected _enabled: boolean = true;
70
protected _checked?: boolean;
71
protected readonly _actionCallback?: (event?: unknown) => unknown;
72
73
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: unknown) => unknown) {
74
super();
75
this._id = id;
76
this._label = label;
77
this._cssClass = cssClass;
78
this._enabled = enabled;
79
this._actionCallback = actionCallback;
80
}
81
82
get id(): string {
83
return this._id;
84
}
85
86
get label(): string {
87
return this._label;
88
}
89
90
set label(value: string) {
91
this._setLabel(value);
92
}
93
94
private _setLabel(value: string): void {
95
if (this._label !== value) {
96
this._label = value;
97
this._onDidChange.fire({ label: value });
98
}
99
}
100
101
get tooltip(): string {
102
return this._tooltip || '';
103
}
104
105
set tooltip(value: string) {
106
this._setTooltip(value);
107
}
108
109
protected _setTooltip(value: string): void {
110
if (this._tooltip !== value) {
111
this._tooltip = value;
112
this._onDidChange.fire({ tooltip: value });
113
}
114
}
115
116
get class(): string | undefined {
117
return this._cssClass;
118
}
119
120
set class(value: string | undefined) {
121
this._setClass(value);
122
}
123
124
protected _setClass(value: string | undefined): void {
125
if (this._cssClass !== value) {
126
this._cssClass = value;
127
this._onDidChange.fire({ class: value });
128
}
129
}
130
131
get enabled(): boolean {
132
return this._enabled;
133
}
134
135
set enabled(value: boolean) {
136
this._setEnabled(value);
137
}
138
139
protected _setEnabled(value: boolean): void {
140
if (this._enabled !== value) {
141
this._enabled = value;
142
this._onDidChange.fire({ enabled: value });
143
}
144
}
145
146
get checked(): boolean | undefined {
147
return this._checked;
148
}
149
150
set checked(value: boolean | undefined) {
151
this._setChecked(value);
152
}
153
154
protected _setChecked(value: boolean | undefined): void {
155
if (this._checked !== value) {
156
this._checked = value;
157
this._onDidChange.fire({ checked: value });
158
}
159
}
160
161
async run(event?: unknown, data?: ITelemetryData): Promise<void> {
162
if (this._actionCallback) {
163
await this._actionCallback(event);
164
}
165
}
166
}
167
168
export interface IRunEvent {
169
readonly action: IAction;
170
readonly error?: Error;
171
}
172
173
export class ActionRunner extends Disposable implements IActionRunner {
174
175
private readonly _onWillRun = this._register(new Emitter<IRunEvent>());
176
get onWillRun() { return this._onWillRun.event; }
177
178
private readonly _onDidRun = this._register(new Emitter<IRunEvent>());
179
get onDidRun() { return this._onDidRun.event; }
180
181
async run(action: IAction, context?: unknown): Promise<void> {
182
if (!action.enabled) {
183
return;
184
}
185
186
this._onWillRun.fire({ action });
187
188
let error: Error | undefined = undefined;
189
try {
190
await this.runAction(action, context);
191
} catch (e) {
192
error = e;
193
}
194
195
this._onDidRun.fire({ action, error });
196
}
197
198
protected async runAction(action: IAction, context?: unknown): Promise<void> {
199
await action.run(context);
200
}
201
}
202
203
export class Separator implements IAction {
204
205
/**
206
* Joins all non-empty lists of actions with separators.
207
*/
208
public static join(...actionLists: readonly IAction[][]) {
209
let out: IAction[] = [];
210
for (const list of actionLists) {
211
if (!list.length) {
212
// skip
213
} else if (out.length) {
214
out = [...out, new Separator(), ...list];
215
} else {
216
out = list;
217
}
218
}
219
220
return out;
221
}
222
223
static readonly ID = 'vs.actions.separator';
224
225
readonly id: string = Separator.ID;
226
227
readonly label: string = '';
228
readonly tooltip: string = '';
229
readonly class: string = 'separator';
230
readonly enabled: boolean = false;
231
readonly checked: undefined = undefined;
232
async run() { }
233
}
234
235
export class SubmenuAction implements IAction {
236
237
readonly id: string;
238
readonly label: string;
239
readonly class: string | undefined;
240
readonly tooltip: string = '';
241
readonly enabled: boolean = true;
242
readonly checked: undefined = undefined;
243
244
private readonly _actions: readonly IAction[];
245
get actions(): readonly IAction[] { return this._actions; }
246
247
constructor(id: string, label: string, actions: readonly IAction[], cssClass?: string) {
248
this.id = id;
249
this.label = label;
250
this.class = cssClass;
251
this._actions = actions;
252
}
253
254
async run(): Promise<void> { }
255
}
256
257
export class EmptySubmenuAction extends Action {
258
259
static readonly ID = 'vs.actions.empty';
260
261
constructor() {
262
super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false);
263
}
264
}
265
266
export function toAction(props: { id: string; label: string; tooltip?: string; enabled?: boolean; checked?: boolean; class?: string; run: Function }): IAction {
267
return {
268
id: props.id,
269
label: props.label,
270
tooltip: props.tooltip ?? props.label,
271
class: props.class,
272
enabled: props.enabled ?? true,
273
checked: props.checked,
274
run: async (...args: unknown[]) => props.run(...args),
275
};
276
}
277
278