Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/actions.ts
3291 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
export class Action extends Disposable implements IAction {
56
57
protected _onDidChange = this._register(new Emitter<IActionChangeEvent>());
58
get onDidChange() { return this._onDidChange.event; }
59
60
protected readonly _id: string;
61
protected _label: string;
62
protected _tooltip: string | undefined;
63
protected _cssClass: string | undefined;
64
protected _enabled: boolean = true;
65
protected _checked?: boolean;
66
protected readonly _actionCallback?: (event?: unknown) => unknown;
67
68
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: unknown) => unknown) {
69
super();
70
this._id = id;
71
this._label = label;
72
this._cssClass = cssClass;
73
this._enabled = enabled;
74
this._actionCallback = actionCallback;
75
}
76
77
get id(): string {
78
return this._id;
79
}
80
81
get label(): string {
82
return this._label;
83
}
84
85
set label(value: string) {
86
this._setLabel(value);
87
}
88
89
private _setLabel(value: string): void {
90
if (this._label !== value) {
91
this._label = value;
92
this._onDidChange.fire({ label: value });
93
}
94
}
95
96
get tooltip(): string {
97
return this._tooltip || '';
98
}
99
100
set tooltip(value: string) {
101
this._setTooltip(value);
102
}
103
104
protected _setTooltip(value: string): void {
105
if (this._tooltip !== value) {
106
this._tooltip = value;
107
this._onDidChange.fire({ tooltip: value });
108
}
109
}
110
111
get class(): string | undefined {
112
return this._cssClass;
113
}
114
115
set class(value: string | undefined) {
116
this._setClass(value);
117
}
118
119
protected _setClass(value: string | undefined): void {
120
if (this._cssClass !== value) {
121
this._cssClass = value;
122
this._onDidChange.fire({ class: value });
123
}
124
}
125
126
get enabled(): boolean {
127
return this._enabled;
128
}
129
130
set enabled(value: boolean) {
131
this._setEnabled(value);
132
}
133
134
protected _setEnabled(value: boolean): void {
135
if (this._enabled !== value) {
136
this._enabled = value;
137
this._onDidChange.fire({ enabled: value });
138
}
139
}
140
141
get checked(): boolean | undefined {
142
return this._checked;
143
}
144
145
set checked(value: boolean | undefined) {
146
this._setChecked(value);
147
}
148
149
protected _setChecked(value: boolean | undefined): void {
150
if (this._checked !== value) {
151
this._checked = value;
152
this._onDidChange.fire({ checked: value });
153
}
154
}
155
156
async run(event?: unknown, data?: ITelemetryData): Promise<void> {
157
if (this._actionCallback) {
158
await this._actionCallback(event);
159
}
160
}
161
}
162
163
export interface IRunEvent {
164
readonly action: IAction;
165
readonly error?: Error;
166
}
167
168
export class ActionRunner extends Disposable implements IActionRunner {
169
170
private readonly _onWillRun = this._register(new Emitter<IRunEvent>());
171
get onWillRun() { return this._onWillRun.event; }
172
173
private readonly _onDidRun = this._register(new Emitter<IRunEvent>());
174
get onDidRun() { return this._onDidRun.event; }
175
176
async run(action: IAction, context?: unknown): Promise<void> {
177
if (!action.enabled) {
178
return;
179
}
180
181
this._onWillRun.fire({ action });
182
183
let error: Error | undefined = undefined;
184
try {
185
await this.runAction(action, context);
186
} catch (e) {
187
error = e;
188
}
189
190
this._onDidRun.fire({ action, error });
191
}
192
193
protected async runAction(action: IAction, context?: unknown): Promise<void> {
194
await action.run(context);
195
}
196
}
197
198
export class Separator implements IAction {
199
200
/**
201
* Joins all non-empty lists of actions with separators.
202
*/
203
public static join(...actionLists: readonly IAction[][]) {
204
let out: IAction[] = [];
205
for (const list of actionLists) {
206
if (!list.length) {
207
// skip
208
} else if (out.length) {
209
out = [...out, new Separator(), ...list];
210
} else {
211
out = list;
212
}
213
}
214
215
return out;
216
}
217
218
static readonly ID = 'vs.actions.separator';
219
220
readonly id: string = Separator.ID;
221
222
readonly label: string = '';
223
readonly tooltip: string = '';
224
readonly class: string = 'separator';
225
readonly enabled: boolean = false;
226
readonly checked: boolean = false;
227
async run() { }
228
}
229
230
export class SubmenuAction implements IAction {
231
232
readonly id: string;
233
readonly label: string;
234
readonly class: string | undefined;
235
readonly tooltip: string = '';
236
readonly enabled: boolean = true;
237
readonly checked: undefined = undefined;
238
239
private readonly _actions: readonly IAction[];
240
get actions(): readonly IAction[] { return this._actions; }
241
242
constructor(id: string, label: string, actions: readonly IAction[], cssClass?: string) {
243
this.id = id;
244
this.label = label;
245
this.class = cssClass;
246
this._actions = actions;
247
}
248
249
async run(): Promise<void> { }
250
}
251
252
export class EmptySubmenuAction extends Action {
253
254
static readonly ID = 'vs.actions.empty';
255
256
constructor() {
257
super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false);
258
}
259
}
260
261
export function toAction(props: { id: string; label: string; tooltip?: string; enabled?: boolean; checked?: boolean; class?: string; run: Function }): IAction {
262
return {
263
id: props.id,
264
label: props.label,
265
tooltip: props.tooltip ?? props.label,
266
class: props.class,
267
enabled: props.enabled ?? true,
268
checked: props.checked,
269
run: async (...args: unknown[]) => props.run(...args),
270
};
271
}
272
273