UNPKG

14.3 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16// *****************************************************************************
17var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21 return c > 3 && r && Object.defineProperty(target, key, r), r;
22};
23var __metadata = (this && this.__metadata) || function (k, v) {
24 if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25};
26var __param = (this && this.__param) || function (paramIndex, decorator) {
27 return function (target, key) { decorator(target, key, paramIndex); }
28};
29Object.defineProperty(exports, "__esModule", { value: true });
30exports.CommandRegistry = exports.CommandService = exports.commandServicePath = exports.CommandContribution = exports.Command = void 0;
31const inversify_1 = require("inversify");
32const event_1 = require("./event");
33const disposable_1 = require("./disposable");
34const contribution_provider_1 = require("./contribution-provider");
35const nls_1 = require("./nls");
36const debounce = require("p-debounce");
37const types_1 = require("./types");
38var Command;
39(function (Command) {
40 /* Determine whether object is a Command */
41 function is(arg) {
42 return (0, types_1.isObject)(arg) && 'id' in arg;
43 }
44 Command.is = is;
45 /** Utility function to easily translate commands */
46 function toLocalizedCommand(command, nlsLabelKey = command.id, nlsCategoryKey) {
47 return Object.assign(Object.assign({}, command), { label: command.label && nls_1.nls.localize(nlsLabelKey, command.label), originalLabel: command.label, category: nlsCategoryKey && command.category && nls_1.nls.localize(nlsCategoryKey, command.category) || command.category, originalCategory: command.category });
48 }
49 Command.toLocalizedCommand = toLocalizedCommand;
50 function toDefaultLocalizedCommand(command) {
51 return Object.assign(Object.assign({}, command), { label: command.label && nls_1.nls.localizeByDefault(command.label), originalLabel: command.label, category: command.category && nls_1.nls.localizeByDefault(command.category), originalCategory: command.category });
52 }
53 Command.toDefaultLocalizedCommand = toDefaultLocalizedCommand;
54 /** Comparator function for when sorting commands */
55 function compareCommands(a, b) {
56 if (a.label && b.label) {
57 const aCommand = (a.category ? `${a.category}: ${a.label}` : a.label).toLowerCase();
58 const bCommand = (b.category ? `${b.category}: ${b.label}` : b.label).toLowerCase();
59 return (aCommand).localeCompare(bCommand);
60 }
61 else {
62 return 0;
63 }
64 }
65 Command.compareCommands = compareCommands;
66 /**
67 * Determine if two commands are equal.
68 *
69 * @param a the first command for comparison.
70 * @param b the second command for comparison.
71 */
72 function equals(a, b) {
73 return (a.id === b.id &&
74 a.label === b.label &&
75 a.iconClass === b.iconClass &&
76 a.category === b.category);
77 }
78 Command.equals = equals;
79})(Command = exports.Command || (exports.Command = {}));
80exports.CommandContribution = Symbol('CommandContribution');
81exports.commandServicePath = '/services/commands';
82exports.CommandService = Symbol('CommandService');
83/**
84 * The command registry manages commands and handlers.
85 */
86let CommandRegistry = class CommandRegistry {
87 constructor(contributionProvider) {
88 this.contributionProvider = contributionProvider;
89 this._commands = {};
90 this._handlers = {};
91 this.toUnregisterCommands = new Map();
92 // List of recently used commands.
93 this._recent = [];
94 this.onWillExecuteCommandEmitter = new event_1.Emitter();
95 this.onWillExecuteCommand = this.onWillExecuteCommandEmitter.event;
96 this.onDidExecuteCommandEmitter = new event_1.Emitter();
97 this.onDidExecuteCommand = this.onDidExecuteCommandEmitter.event;
98 this.onCommandsChangedEmitter = new event_1.Emitter();
99 this.onCommandsChanged = this.onCommandsChangedEmitter.event;
100 this.fireDidChange = debounce(() => this.doFireDidChange(), 0);
101 }
102 onStart() {
103 const contributions = this.contributionProvider.getContributions();
104 for (const contrib of contributions) {
105 contrib.registerCommands(this);
106 }
107 }
108 *getAllCommands() {
109 var _a;
110 for (const command of Object.values(this._commands)) {
111 yield Object.assign(Object.assign({}, command), { handlers: (_a = this._handlers[command.id]) !== null && _a !== void 0 ? _a : [] });
112 }
113 }
114 /**
115 * Register the given command and handler if present.
116 *
117 * Throw if a command is already registered for the given command identifier.
118 */
119 registerCommand(command, handler) {
120 if (this._commands[command.id]) {
121 console.warn(`A command ${command.id} is already registered.`);
122 return disposable_1.Disposable.NULL;
123 }
124 const toDispose = new disposable_1.DisposableCollection(this.doRegisterCommand(command));
125 if (handler) {
126 toDispose.push(this.registerHandler(command.id, handler));
127 }
128 this.toUnregisterCommands.set(command.id, toDispose);
129 toDispose.push(disposable_1.Disposable.create(() => this.toUnregisterCommands.delete(command.id)));
130 return toDispose;
131 }
132 doRegisterCommand(command) {
133 this._commands[command.id] = command;
134 return {
135 dispose: () => {
136 delete this._commands[command.id];
137 }
138 };
139 }
140 unregisterCommand(commandOrId) {
141 const id = Command.is(commandOrId) ? commandOrId.id : commandOrId;
142 const toUnregister = this.toUnregisterCommands.get(id);
143 if (toUnregister) {
144 toUnregister.dispose();
145 }
146 }
147 /**
148 * Register the given handler for the given command identifier.
149 *
150 * If there is already a handler for the given command
151 * then the given handler is registered as more specific, and
152 * has higher priority during enablement, visibility and toggle state evaluations.
153 */
154 registerHandler(commandId, handler) {
155 let handlers = this._handlers[commandId];
156 if (!handlers) {
157 this._handlers[commandId] = handlers = [];
158 }
159 handlers.unshift(handler);
160 this.fireDidChange();
161 return {
162 dispose: () => {
163 const idx = handlers.indexOf(handler);
164 if (idx >= 0) {
165 handlers.splice(idx, 1);
166 this.fireDidChange();
167 }
168 }
169 };
170 }
171 doFireDidChange() {
172 this.onCommandsChangedEmitter.fire();
173 }
174 /**
175 * Test whether there is an active handler for the given command.
176 */
177 // eslint-disable-next-line @typescript-eslint/no-explicit-any
178 isEnabled(command, ...args) {
179 return typeof this.getActiveHandler(command, ...args) !== 'undefined';
180 }
181 /**
182 * Test whether there is a visible handler for the given command.
183 */
184 // eslint-disable-next-line @typescript-eslint/no-explicit-any
185 isVisible(command, ...args) {
186 return typeof this.getVisibleHandler(command, ...args) !== 'undefined';
187 }
188 /**
189 * Test whether there is a toggled handler for the given command.
190 */
191 // eslint-disable-next-line @typescript-eslint/no-explicit-any
192 isToggled(command, ...args) {
193 return typeof this.getToggledHandler(command, ...args) !== 'undefined';
194 }
195 /**
196 * Execute the active handler for the given command and arguments.
197 *
198 * Reject if a command cannot be executed.
199 */
200 // eslint-disable-next-line @typescript-eslint/no-explicit-any
201 async executeCommand(commandId, ...args) {
202 const handler = this.getActiveHandler(commandId, ...args);
203 if (handler) {
204 await this.fireWillExecuteCommand(commandId, args);
205 const result = await handler.execute(...args);
206 this.onDidExecuteCommandEmitter.fire({ commandId, args });
207 return result;
208 }
209 throw Object.assign(new Error(`The command '${commandId}' cannot be executed. There are no active handlers available for the command.`), { code: 'NO_ACTIVE_HANDLER' });
210 }
211 // eslint-disable-next-line @typescript-eslint/no-explicit-any
212 async fireWillExecuteCommand(commandId, args = []) {
213 await event_1.WaitUntilEvent.fire(this.onWillExecuteCommandEmitter, { commandId, args }, 30000);
214 }
215 /**
216 * Get a visible handler for the given command or `undefined`.
217 */
218 // eslint-disable-next-line @typescript-eslint/no-explicit-any
219 getVisibleHandler(commandId, ...args) {
220 const handlers = this._handlers[commandId];
221 if (handlers) {
222 for (const handler of handlers) {
223 try {
224 if (!handler.isVisible || handler.isVisible(...args)) {
225 return handler;
226 }
227 }
228 catch (error) {
229 console.error(error);
230 }
231 }
232 }
233 return undefined;
234 }
235 /**
236 * Get an active handler for the given command or `undefined`.
237 */
238 // eslint-disable-next-line @typescript-eslint/no-explicit-any
239 getActiveHandler(commandId, ...args) {
240 const handlers = this._handlers[commandId];
241 if (handlers) {
242 for (const handler of handlers) {
243 try {
244 if (!handler.isEnabled || handler.isEnabled(...args)) {
245 return handler;
246 }
247 }
248 catch (error) {
249 console.error(error);
250 }
251 }
252 }
253 return undefined;
254 }
255 /**
256 * Get a toggled handler for the given command or `undefined`.
257 */
258 // eslint-disable-next-line @typescript-eslint/no-explicit-any
259 getToggledHandler(commandId, ...args) {
260 const handlers = this._handlers[commandId];
261 if (handlers) {
262 for (const handler of handlers) {
263 try {
264 if (handler.isToggled && handler.isToggled(...args)) {
265 return handler;
266 }
267 }
268 catch (error) {
269 console.error(error);
270 }
271 }
272 }
273 return undefined;
274 }
275 /**
276 * Returns with all handlers for the given command. If the command does not have any handlers,
277 * or the command is not registered, returns an empty array.
278 */
279 getAllHandlers(commandId) {
280 const handlers = this._handlers[commandId];
281 return handlers ? handlers.slice() : [];
282 }
283 /**
284 * Get all registered commands.
285 */
286 get commands() {
287 return Object.values(this._commands);
288 }
289 /**
290 * Get a command for the given command identifier.
291 */
292 getCommand(id) {
293 return this._commands[id];
294 }
295 /**
296 * Get all registered commands identifiers.
297 */
298 get commandIds() {
299 return Object.keys(this._commands);
300 }
301 /**
302 * Get the list of recently used commands.
303 */
304 get recent() {
305 const commands = [];
306 for (const recentId of this._recent) {
307 const command = this.getCommand(recentId);
308 if (command) {
309 commands.push(command);
310 }
311 }
312 return commands;
313 }
314 /**
315 * Set the list of recently used commands.
316 * @param commands the list of recently used commands.
317 */
318 set recent(commands) {
319 this._recent = Array.from(new Set(commands.map(e => e.id)));
320 }
321 /**
322 * Adds a command to recently used list.
323 * Prioritizes commands that were recently executed to be most recent.
324 *
325 * @param recent a recent command, or array of recent commands.
326 */
327 addRecentCommand(recent) {
328 for (const recentCommand of Array.isArray(recent) ? recent : [recent]) {
329 // Determine if the command currently exists in the recently used list.
330 const index = this._recent.findIndex(commandId => commandId === recentCommand.id);
331 // If the command exists, remove it from the array so it can later be placed at the top.
332 if (index >= 0) {
333 this._recent.splice(index, 1);
334 }
335 // Add the recent command to the beginning of the array (most recent).
336 this._recent.unshift(recentCommand.id);
337 }
338 }
339 /**
340 * Clear the list of recently used commands.
341 */
342 clearCommandHistory() {
343 this.recent = [];
344 }
345};
346CommandRegistry = __decorate([
347 (0, inversify_1.injectable)(),
348 __param(0, (0, inversify_1.inject)(contribution_provider_1.ContributionProvider)),
349 __param(0, (0, inversify_1.named)(exports.CommandContribution)),
350 __metadata("design:paramtypes", [Object])
351], CommandRegistry);
352exports.CommandRegistry = CommandRegistry;
353//# sourceMappingURL=command.js.map
\No newline at end of file