@opensumi/ide-core-common 2.21.13 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/lib/command.d.ts +15 -5
  2. package/lib/command.d.ts.map +1 -1
  3. package/lib/command.js +11 -1
  4. package/lib/command.js.map +1 -1
  5. package/lib/const/application.d.ts +6 -1
  6. package/lib/const/application.d.ts.map +1 -1
  7. package/lib/const/application.js +8 -3
  8. package/lib/const/application.js.map +1 -1
  9. package/lib/declare.d.ts +2 -2
  10. package/lib/declare.d.ts.map +1 -1
  11. package/lib/editor.d.ts +4 -0
  12. package/lib/editor.d.ts.map +1 -1
  13. package/lib/electron.d.ts +1 -1
  14. package/lib/electron.d.ts.map +1 -1
  15. package/lib/event-bus/event-bus-types.d.ts +1 -1
  16. package/lib/event-bus/event-bus-types.d.ts.map +1 -1
  17. package/lib/event-bus/event-bus.js.map +1 -1
  18. package/lib/event-bus/event-decorator.js.map +1 -1
  19. package/lib/extension.schema.d.ts +1 -1
  20. package/lib/extension.schema.d.ts.map +1 -1
  21. package/lib/hash-calculate/WASMInterface.d.ts +2 -2
  22. package/lib/hash-calculate/WASMInterface.d.ts.map +1 -1
  23. package/lib/hash-calculate/hash-calculate.js.map +1 -1
  24. package/lib/hash-calculate/util.d.ts +2 -2
  25. package/lib/hash-calculate/util.d.ts.map +1 -1
  26. package/lib/json-schema.d.ts +9 -0
  27. package/lib/json-schema.d.ts.map +1 -1
  28. package/lib/keyboard/keymap.d.ts +1 -1
  29. package/lib/keyboard/keymap.d.ts.map +1 -1
  30. package/lib/localize.d.ts +1 -1
  31. package/lib/localize.d.ts.map +1 -1
  32. package/lib/log.d.ts +2 -2
  33. package/lib/log.d.ts.map +1 -1
  34. package/lib/mime.d.ts +6 -6
  35. package/lib/module.js.map +1 -1
  36. package/lib/preferences/preference-schema.d.ts +13 -3
  37. package/lib/preferences/preference-schema.d.ts.map +1 -1
  38. package/lib/preferences/preference-schema.js +11 -1
  39. package/lib/preferences/preference-schema.js.map +1 -1
  40. package/lib/preferences/preference-scope.d.ts +8 -1
  41. package/lib/preferences/preference-scope.d.ts.map +1 -1
  42. package/lib/preferences/preference-scope.js +11 -2
  43. package/lib/preferences/preference-scope.js.map +1 -1
  44. package/lib/problem-matcher.d.ts +1 -1
  45. package/lib/problem-matcher.d.ts.map +1 -1
  46. package/lib/problem-matcher.js.map +1 -1
  47. package/lib/problem-pattern.d.ts +4 -2
  48. package/lib/problem-pattern.d.ts.map +1 -1
  49. package/lib/problem-pattern.js +24 -1
  50. package/lib/problem-pattern.js.map +1 -1
  51. package/lib/reference.d.ts +2 -1
  52. package/lib/reference.d.ts.map +1 -1
  53. package/lib/reference.js +11 -6
  54. package/lib/reference.js.map +1 -1
  55. package/lib/reporter.js.map +1 -1
  56. package/lib/storage.d.ts +1 -1
  57. package/lib/storage.d.ts.map +1 -1
  58. package/lib/storage.js.map +1 -1
  59. package/lib/task-definition.js.map +1 -1
  60. package/lib/types/common.d.ts +1 -1
  61. package/lib/types/common.d.ts.map +1 -1
  62. package/lib/types/editor.d.ts +2 -2
  63. package/lib/types/editor.d.ts.map +1 -1
  64. package/lib/types/file-watch.d.ts +1 -1
  65. package/lib/types/file-watch.d.ts.map +1 -1
  66. package/lib/types/markers/markers-manager.d.ts.map +1 -1
  67. package/lib/types/markers/markers-manager.js +4 -10
  68. package/lib/types/markers/markers-manager.js.map +1 -1
  69. package/lib/types/markers/markers.d.ts +2 -0
  70. package/lib/types/markers/markers.d.ts.map +1 -1
  71. package/lib/types/markers/markers.js.map +1 -1
  72. package/package.json +7 -6
  73. package/src/LifeCyclePhase.ts +6 -0
  74. package/src/application-error.ts +77 -0
  75. package/src/application-props.ts +28 -0
  76. package/src/application.lifecycle.ts +15 -0
  77. package/src/browser-fs/ensure-dir.ts +46 -0
  78. package/src/browser-fs/util.ts +52 -0
  79. package/src/clipboard.ts +31 -0
  80. package/src/command.ts +726 -0
  81. package/src/commands/terminal.ts +0 -0
  82. package/src/comparers.ts +152 -0
  83. package/src/connection.ts +7 -0
  84. package/src/const/application.ts +11 -0
  85. package/src/const/index.ts +1 -0
  86. package/src/contribution-provider.ts +47 -0
  87. package/src/credential.ts +21 -0
  88. package/src/crypto.ts +8 -0
  89. package/src/declare.ts +3 -0
  90. package/src/di-helper/domain-helper.ts +48 -0
  91. package/src/di-helper/index.ts +1 -0
  92. package/src/editor.ts +6 -0
  93. package/src/electron.ts +168 -0
  94. package/src/env.ts +1 -0
  95. package/src/event-bus/basic-event.ts +3 -0
  96. package/src/event-bus/event-bus-types.ts +68 -0
  97. package/src/event-bus/event-bus.ts +57 -0
  98. package/src/event-bus/event-decorator.ts +32 -0
  99. package/src/event-bus/index.ts +4 -0
  100. package/src/extension.schema.ts +16 -0
  101. package/src/hash-calculate/WASMInterface.ts +266 -0
  102. package/src/hash-calculate/hash-calculate.ts +53 -0
  103. package/src/hash-calculate/lockedCreate.ts +17 -0
  104. package/src/hash-calculate/md5.wasm.json +1 -0
  105. package/src/hash-calculate/mutex.ts +30 -0
  106. package/src/hash-calculate/util.ts +190 -0
  107. package/src/id-generator.ts +15 -0
  108. package/src/index.ts +39 -0
  109. package/src/json-schema.ts +80 -0
  110. package/src/keyboard/index.ts +2 -0
  111. package/src/keyboard/keyboard-layout-provider.ts +46 -0
  112. package/src/keyboard/keymap.ts +224 -0
  113. package/src/line-text.ts +93 -0
  114. package/src/localize.ts +203 -0
  115. package/src/log.ts +338 -0
  116. package/src/markdown.ts +8 -0
  117. package/src/menu.ts +31 -0
  118. package/src/mime.ts +8 -0
  119. package/src/mocks/electron/browserMock.ts +10 -0
  120. package/src/mocks/electron/ipcRenderer.ts +30 -0
  121. package/src/module.ts +31 -0
  122. package/src/network.ts +47 -0
  123. package/src/node/cli.ts +5 -0
  124. package/src/node/port.ts +134 -0
  125. package/src/preferences/index.ts +2 -0
  126. package/src/preferences/preference-schema.ts +122 -0
  127. package/src/preferences/preference-scope.ts +65 -0
  128. package/src/problem-matcher.ts +389 -0
  129. package/src/problem-pattern.ts +574 -0
  130. package/src/range.ts +24 -0
  131. package/src/reference.ts +141 -0
  132. package/src/reporter.ts +64 -0
  133. package/src/settings/general.ts +5 -0
  134. package/src/settings/index.ts +3 -0
  135. package/src/settings/search.ts +7 -0
  136. package/src/settings/terminal.ts +9 -0
  137. package/src/storage.ts +99 -0
  138. package/src/task-definition.ts +172 -0
  139. package/src/theme.ts +7 -0
  140. package/src/types/accessibility.ts +4 -0
  141. package/src/types/application.ts +14 -0
  142. package/src/types/authentication.ts +120 -0
  143. package/src/types/common.ts +14 -0
  144. package/src/types/dnd.ts +8 -0
  145. package/src/types/editor.ts +246 -0
  146. package/src/types/extension.ts +96 -0
  147. package/src/types/file-watch.ts +100 -0
  148. package/src/types/file.ts +262 -0
  149. package/src/types/index.ts +15 -0
  150. package/src/types/markdown.ts +10 -0
  151. package/src/types/markers/index.ts +2 -0
  152. package/src/types/markers/markers-manager.ts +392 -0
  153. package/src/types/markers/markers.ts +125 -0
  154. package/src/types/message.ts +6 -0
  155. package/src/types/reporter.ts +96 -0
  156. package/src/types/statusbar.ts +6 -0
  157. package/src/types/string.ts +3 -0
  158. package/src/utils/index.ts +1 -0
  159. package/src/utils/ipc.ts +28 -0
package/src/command.ts ADDED
@@ -0,0 +1,726 @@
1
+ import { Autowired, Injectable } from '@opensumi/di';
2
+ import { Disposable, Emitter, Event, IDisposable, MaybePromise } from '@opensumi/ide-utils';
3
+
4
+ import { ContributionProvider } from './contribution-provider';
5
+ import { replaceLocalizePlaceholder } from './localize';
6
+ import { getDebugLogger } from './log';
7
+ import { IExtensionInfo } from './types';
8
+
9
+ export interface Command {
10
+ /**
11
+ * 命令 id,全局唯一
12
+ */
13
+ id: string;
14
+ /**
15
+ * 要在命令面板显示的文案
16
+ * 支持国际化占位符,例如 %evenEditorGroups%
17
+ */
18
+ label?: string;
19
+ /**
20
+ * 要在命令面板显示的较短的文案
21
+ * 支持国际化占位符,例如 %evenEditorGroups%
22
+ */
23
+ shortLabel?: string;
24
+ /**
25
+ * 要在命令面板显示的图标
26
+ */
27
+ iconClass?: string;
28
+ /**
29
+ * 要在命令面板显示的图标
30
+ */
31
+ toogleIconClass?: string;
32
+ /**
33
+ * 要在命令面板显示的分组
34
+ * 支持国际化占位符,例如 %evenEditorGroups%
35
+ */
36
+ category?: string;
37
+
38
+ /**
39
+ * 代理执行的命令
40
+ */
41
+ delegate?: string;
42
+
43
+ /**
44
+ * 在任意语言下都相同的别名
45
+ */
46
+ alias?: string;
47
+ /**
48
+ * 在任意语言下都相同的分类名
49
+ */
50
+ aliasCategory?: string;
51
+ /**
52
+ * 是否启用该命令,值为 when 表达式
53
+ * 这个值只影响 UI 是否展示 (命令面板或者菜单)
54
+ */
55
+ enablement?: string;
56
+ }
57
+
58
+ /**
59
+ * Command 的工具方法
60
+ */
61
+ export namespace Command {
62
+ /**
63
+ * 判断是否是命令
64
+ * @param arg 要判断的对象
65
+ */
66
+ export function is(arg: Command | any): arg is Command {
67
+ return !!arg && arg === Object(arg) && 'id' in arg;
68
+ }
69
+
70
+ /**
71
+ * 比较两个命令是否相等
72
+ * 用于命令面板的排序
73
+ * @param a 待比较的命令
74
+ * @param b 待比较的命令
75
+ */
76
+ export function compareCommands(a: Command, b: Command): number {
77
+ if (a.label && b.label) {
78
+ const aCommand = a.category ? a.category + a.label : a.label;
79
+ const bCommand = b.category ? b.category + b.label : b.label;
80
+ return aCommand.localeCompare(bCommand);
81
+ } else {
82
+ return 0;
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 命令处理函数接口
89
+ */
90
+ export interface CommandHandler<T = any> {
91
+ /**
92
+ * 命令执行函数
93
+ * @param args 传递的参数
94
+ */
95
+ execute: T;
96
+ /**
97
+ * 命令是否启用
98
+ * 若为否,则不会被执行到
99
+ * 并且命令面板也不会显示
100
+ * @param args 传递的参数
101
+ */
102
+ isEnabled?(...args: any[]): boolean;
103
+ /**
104
+ * 命令是否可见
105
+ * 若为否,则不会再命令面板显示
106
+ * 但是可以被外部执行
107
+ * @param args 传递的参数
108
+ */
109
+ isVisible?(...args: any[]): boolean;
110
+ /**
111
+ * 是否可切换
112
+ * 主要给菜单使用
113
+ * @param args
114
+ */
115
+ isToggled?(...args: any[]): boolean;
116
+ /**
117
+ * 获取鉴过权的命令处理函数
118
+ * @param commandId 命令 id
119
+ * @param extensionInfo 插件的主要属性
120
+ * @param args 命令其他参数
121
+ */
122
+ isPermitted?(extensionInfo: IExtensionInfo, ...args: any[]): boolean;
123
+ }
124
+
125
+ export const CommandContribution = Symbol('CommandContribution');
126
+
127
+ /**
128
+ * 命令贡献 Contribution
129
+ * 其他模块如果要注册命令需要实现该接口,并且把 Symbol 定义加入 Domain
130
+ */
131
+ export interface CommandContribution {
132
+ /**
133
+ * 注册命令
134
+ */
135
+ registerCommands(commands: CommandRegistry): void;
136
+ }
137
+
138
+ type PostInterceptorFunction = (result: any) => MaybePromise<any>;
139
+ type PreInterceptorFunction = (args: any[]) => MaybePromise<any[] | boolean>;
140
+
141
+ export type PreCommandInterceptor = (command: string, args: any[]) => MaybePromise<any[] | boolean>;
142
+ export type PostCommandInterceptor = (command: string, result: any) => MaybePromise<any>;
143
+
144
+ export const CommandService = Symbol('CommandService');
145
+ export const CommandRegistry = Symbol('CommandRegistry');
146
+
147
+ export const HANDLER_NOT_FOUND = 'HANDLER_NOT_FOUND';
148
+
149
+ export interface ICommandEvent {
150
+ commandId: string;
151
+ args: any[];
152
+ }
153
+
154
+ /**
155
+ * 命令执行模块
156
+ */
157
+ export interface CommandService {
158
+ executeCommand<T>(commandId: string, ...args: any[]): Promise<T | undefined>;
159
+ /**
160
+ * 执行命令将报错 catch 并 log 输出
161
+ */
162
+ tryExecuteCommand<T>(commandId: string, ...args: any[]): Promise<T | undefined>;
163
+ onWillExecuteCommand: Event<ICommandEvent>;
164
+ onDidExecuteCommand: Event<ICommandEvent>;
165
+ }
166
+
167
+ /**
168
+ * 命令注册和管理模块
169
+ */
170
+ interface CoreCommandRegistry {
171
+ /**
172
+ * 注册命令
173
+ * @param command 要注册的命令
174
+ * @param handler 要绑定的执行函数,可延后添加
175
+ * @returns 销毁命令的方法
176
+ */
177
+ registerCommand<T = any>(command: Command, handler?: CommandHandler<T>): IDisposable;
178
+ updateCommandDetailById(id: string, command: Partial<Command>): void;
179
+ /**
180
+ * 解绑命令
181
+ * @param commandOrId 要解绑命令或命令 id
182
+ */
183
+ unregisterCommand(commandOrId: Command | string): void;
184
+ /**
185
+ * 给命令注册处理函数
186
+ * 可以给命令加多个处理函数
187
+ * @param commandId 要添加的命令 id
188
+ * @param handler 要添加的处理函数
189
+ */
190
+ registerHandler<T = any>(commandId: string, handler: CommandHandler<T>): IDisposable;
191
+ /**
192
+ * 通过命令 id 获取命令
193
+ * @param commandId 命令 id
194
+ */
195
+ getCommand(commandId: string): Command | undefined;
196
+ getRawCommand(commandId: string): Command | undefined;
197
+ /**
198
+ * 获取所有命令
199
+ */
200
+ getCommands(): Command[];
201
+
202
+ /**
203
+ * 判断命令是否启用
204
+ * @param commandId 命令 id
205
+ * @param args 传递参数
206
+ */
207
+ isEnabled(commandId: string, ...args: any[]): boolean;
208
+ /**
209
+ * 判断命令是否可见
210
+ * @param commandId 命令 id
211
+ * @param args 传递参数
212
+ */
213
+ isVisible(commandId: string, ...args: any[]): boolean;
214
+ /**
215
+ * 判断命令是否可切换
216
+ * @param commandId 命令 id
217
+ */
218
+ isToggled(commandId: string, ...args: any[]): boolean;
219
+ /**
220
+ * 判断命令是否启用
221
+ * @param commandId 命令 id
222
+ * @param args
223
+ */
224
+ getActiveHandler(commandId: string, ...args: any[]): CommandHandler | undefined;
225
+ /**
226
+ * 获取最近使用的命令列表
227
+ */
228
+ getRecentCommands(): Command[];
229
+ /**
230
+ * 设置最近使用的命令列表
231
+ */
232
+ setRecentCommands(commands: Command[]): Command[];
233
+
234
+ /**
235
+ * 为特定的命令注册前置拦截器
236
+ *
237
+ * 拦截器的入参是该命令的参数数组,请返回修改后的参数数组或者返回 false 来拦截本次执行
238
+ */
239
+ beforeExecuteCommand(commandId: string, interceptorFunc: PreInterceptorFunction): IDisposable;
240
+ /**
241
+ * 可以为命令添加拦截器
242
+ * 拦截器的入参是该命令的参数数组,请返回修改后的参数数组或者返回 false 来拦截本次执行
243
+ * @param interceptor 每个 command 都会执行一遍
244
+ */
245
+ beforeExecuteCommand(interceptor: PreCommandInterceptor): IDisposable;
246
+ afterExecuteCommand(commandId: string, interceptorFunc: PostInterceptorFunction): IDisposable;
247
+ afterExecuteCommand(interceptor: PostCommandInterceptor): IDisposable;
248
+ /**
249
+ * 是否是通过鉴过权的命令
250
+ * @param commandId
251
+ * @param extensionInfo
252
+ * @param args
253
+ */
254
+ isPermittedCommand(commandId: string, extensionInfo: IExtensionInfo, ...args: any[]): boolean;
255
+ }
256
+
257
+ export interface CommandRegistry extends CoreCommandRegistry {
258
+ /**
259
+ * 从 ContributionProvide 中拿到执行命令 Contributuon
260
+ * 执行注册操作
261
+ */
262
+ initialize(): void;
263
+ }
264
+
265
+ // 不带 contribution 的 CommandRegistry
266
+ @Injectable()
267
+ export class CoreCommandRegistryImpl implements CoreCommandRegistry {
268
+ protected readonly _commands: { [id: string]: Command } = {};
269
+ protected readonly _handlers: { [id: string]: CommandHandler[] } = {};
270
+
271
+ protected readonly unregisterCommands = new Map<string, Disposable>();
272
+ // 最近执行的命令列表
273
+ protected readonly _recent: Command[] = [];
274
+
275
+ public readonly preCommandInterceptors: PreCommandInterceptor[] = [];
276
+
277
+ public readonly postCommandInterceptors: PostCommandInterceptor[] = [];
278
+
279
+ private readonly preCommandInterceptorMap = new Map<string, PreInterceptorFunction[]>();
280
+ private readonly postCommandInterceptor = new Map<string, PostInterceptorFunction[]>();
281
+
282
+ private readonly logger = getDebugLogger();
283
+
284
+ /**
285
+ * 命令执行方法
286
+ * @param commandId 命令执行方法
287
+ * @param args
288
+ */
289
+ async executeCommand<T>(commandId: string, ...args: any[]): Promise<T | undefined> {
290
+ const command = this.getCommand(commandId);
291
+ // 执行代理命令。即该命令是另一个命令的 alias,这里转为执行真实的命令
292
+ if (command && command.delegate) {
293
+ return this.executeCommand<T>(command.delegate, ...args);
294
+ }
295
+
296
+ // 把 before 在 handler 判断前置,对于 onCommand 激活的插件如果没有在 contributes 配置,那么 handler 是不存在的,也就无法激活
297
+ // 如 node debug 插件 https://github.com/microsoft/vscode-node-debug/blob/main/package.json
298
+ const _preCommandInterceptor = this.preCommandInterceptorMap.get(commandId);
299
+ const preInterceptorWrapper = (_preCommandInterceptor ?? []).map((cb) => (_, args) => cb(args));
300
+ const currentPreInterceptors = [...preInterceptorWrapper, ...this.preCommandInterceptors];
301
+ for (const preInterceptor of currentPreInterceptors) {
302
+ const result = await preInterceptor(commandId, args);
303
+ if (result === false) {
304
+ this.logger.log(`command ${commandId} is prevented by pre interceptor`, preInterceptor.name);
305
+ return undefined;
306
+ } else if (Array.isArray(result)) {
307
+ args = result;
308
+ }
309
+ }
310
+ const handler = this.getActiveHandler(commandId, ...args);
311
+ if (handler) {
312
+ let result = await handler.execute(...args);
313
+ const commandInterceptor = this.postCommandInterceptor.get(commandId);
314
+ if (commandInterceptor) {
315
+ for (const postInterceptor of commandInterceptor) {
316
+ result = await postInterceptor(result);
317
+ }
318
+ }
319
+ for (const postCommand of this.postCommandInterceptors) {
320
+ result = await postCommand(commandId, result);
321
+ }
322
+ return result;
323
+ }
324
+ let argsMessage = '';
325
+ try {
326
+ argsMessage = args && args.length > 0 ? ` (args: ${JSON.stringify(args)})` : '';
327
+ } catch (e) {
328
+ argsMessage = 'args cannot be convert to JSON';
329
+ }
330
+ const err = new Error(
331
+ `The command '${commandId}' cannot be executed. There are no active handlers available for the command.${argsMessage}`,
332
+ );
333
+ err.name = HANDLER_NOT_FOUND;
334
+ throw err;
335
+ }
336
+
337
+ /**
338
+ * 获取所有命令
339
+ */
340
+ getCommands(): Command[] {
341
+ return Object.keys(this._commands).map((id) => this.getCommand(id)!);
342
+ }
343
+
344
+ /**
345
+ * 注册命令,命令不能重复注册
346
+ * @param command
347
+ * @param handler
348
+ * @returns 命令销毁函数
349
+ */
350
+ registerCommand<T>(command: Command, handler?: CommandHandler<T>): IDisposable {
351
+ if (this._commands[command.id]) {
352
+ this.logger.warn(`A command ${command.id} is already registered.`);
353
+ return Disposable.NULL;
354
+ }
355
+ const toDispose = new Disposable();
356
+ // 添加命令的销毁函数
357
+ toDispose.addDispose(this.doRegisterCommand(command));
358
+ if (handler) {
359
+ // 添加处理函数的销毁函数
360
+ toDispose.addDispose(this.registerHandler(command.id, handler));
361
+ }
362
+ // 添加解绑时的销毁逻辑
363
+ this.unregisterCommands.set(command.id, toDispose);
364
+ toDispose.addDispose(Disposable.create(() => this.unregisterCommands.delete(command.id)));
365
+ return toDispose;
366
+ }
367
+
368
+ /**
369
+ * 有时候我们需要在命令注册后才更新其描述。
370
+ *
371
+ * 比如说我们会为所有的 ContainerId 注册 Toggle Panel 的命令,但我们不想在 QuickOpen 中展示所有 ContainerId 对应的命令
372
+ * 这个时候我们可以先注册所有命令,然后再更新所有 Command 的描述
373
+ * @param id 命令 ID
374
+ * @param command
375
+ */
376
+ updateCommandDetailById(id: string, command: Partial<Command>): void {
377
+ if (!this._commands[id]) {
378
+ this.logger.warn(`Command ${id} not found.`);
379
+ return;
380
+ }
381
+
382
+ this._commands[id] = {
383
+ ...this._commands[id],
384
+ ...command,
385
+ };
386
+ }
387
+
388
+ /**
389
+ * 解绑命令
390
+ * @param command 命令
391
+ */
392
+ unregisterCommand(command: Command): void;
393
+ /**
394
+ * 解绑命令
395
+ * @param id 命令 id
396
+ */
397
+ unregisterCommand(commandOrId: Command | string): void {
398
+ const id = Command.is(commandOrId) ? commandOrId.id : commandOrId;
399
+ const unregisterCommand = this.unregisterCommands.get(id);
400
+ if (unregisterCommand) {
401
+ unregisterCommand.dispose();
402
+ }
403
+ }
404
+
405
+ /**
406
+ * 给命令注册处理函数
407
+ * 可以给命令加多个处理函数
408
+ * 默认后面注册的优先级更高
409
+ * @param commandId 要添加的命令 id
410
+ * @param handler 要添加的处理函数
411
+ * @returns 销毁函数
412
+ */
413
+ registerHandler<T>(commandId: string, handler: CommandHandler<T>): IDisposable {
414
+ let handlers = this._handlers[commandId];
415
+ if (!handlers) {
416
+ this._handlers[commandId] = handlers = [];
417
+ }
418
+ handlers.unshift(handler);
419
+ return {
420
+ dispose: () => {
421
+ const idx = handlers.indexOf(handler);
422
+ if (idx >= 0) {
423
+ handlers.splice(idx, 1);
424
+ }
425
+ },
426
+ };
427
+ }
428
+
429
+ /**
430
+ * 判断命令是否启用
431
+ * @param commandId 命令 id
432
+ * @param args 传递参数
433
+ */
434
+ isEnabled(command: string, ...args: any[]): boolean {
435
+ if (!this._handlers[command]) {
436
+ // 对于插件package.json中注册的command,会没有handler,
437
+ // 但是它应该可用,这样才能让插件在未启动的情况下点击菜单
438
+ return true;
439
+ }
440
+ return this.getActiveHandler(command, ...args) !== undefined;
441
+ }
442
+
443
+ /**
444
+ * 判断命令是否可见
445
+ * @param commandId 命令 id
446
+ * @param args 传递参数
447
+ */
448
+ isVisible(command: string, ...args: any[]): boolean {
449
+ if (!this._handlers[command]) {
450
+ // 对于插件package.json中注册的command,会没有handler,
451
+ // 但是它应该可见,这样才能让插件在未启动的情况下显示菜单
452
+ return true;
453
+ }
454
+ return this.getVisibleHandler(command, ...args) !== undefined;
455
+ }
456
+
457
+ /**
458
+ * 是否可切换
459
+ * 主要给菜单使用
460
+ * @param commandId 命令 id
461
+ * @param args 传递参数
462
+ */
463
+ isToggled(command: string, ...args: any[]): boolean {
464
+ const handler = this.getToggledHandler(command);
465
+ return handler && handler.isToggled ? handler.isToggled(...args) : false;
466
+ }
467
+
468
+ /**
469
+ * 获取可见的命令处理函数
470
+ * @param commandId 命令 id
471
+ * @param args 传递参数
472
+ */
473
+ protected getVisibleHandler(commandId: string, ...args: any[]): CommandHandler | undefined {
474
+ const handlers = this._handlers[commandId];
475
+ if (handlers) {
476
+ for (const handler of handlers) {
477
+ if (!handler.isVisible || handler.isVisible(...args)) {
478
+ return handler;
479
+ }
480
+ }
481
+ }
482
+ return undefined;
483
+ }
484
+
485
+ /**
486
+ * 获取启用的命令处理函数
487
+ * @param commandId 命令 id
488
+ * @param args
489
+ */
490
+ getActiveHandler(commandId: string, ...args: any[]): CommandHandler | undefined {
491
+ const command = this.getCommand(commandId);
492
+ if (command && command.delegate) {
493
+ return this.getActiveHandler(command.delegate, ...args);
494
+ }
495
+ const handlers = this._handlers[commandId];
496
+ if (handlers) {
497
+ for (const handler of handlers) {
498
+ if (!handler.isEnabled || handler.isEnabled(...args)) {
499
+ return handler;
500
+ }
501
+ }
502
+ }
503
+ return undefined;
504
+ }
505
+
506
+ /**
507
+ * 获取鉴过权的命令处理函数
508
+ * @param commandId 命令 id
509
+ * @param extensionInfo 插件的主要属性
510
+ * @param args 命令其他参数
511
+ */
512
+ isPermittedCommand(commandId: string, extensionInfo: IExtensionInfo, ...args: any[]): boolean {
513
+ const command = this.getCommand(commandId);
514
+ if (command && command.delegate) {
515
+ return this.isPermittedCommand(command.delegate, extensionInfo, ...args);
516
+ }
517
+ const handlers = this._handlers[commandId];
518
+ return (
519
+ !Array.isArray(handlers) ||
520
+ handlers.every((handler) => !handler.isPermitted || handler.isPermitted(extensionInfo, ...args))
521
+ );
522
+ }
523
+
524
+ /**
525
+ * 获取可切换的命令处理函数
526
+ * @param commandId 命令 id
527
+ */
528
+ protected getToggledHandler(commandId: string): CommandHandler | undefined {
529
+ const handlers = this._handlers[commandId];
530
+ if (handlers) {
531
+ for (const handler of handlers) {
532
+ if (handler.isToggled) {
533
+ return handler;
534
+ }
535
+ }
536
+ }
537
+ return undefined;
538
+ }
539
+
540
+ /**
541
+ * 解决语言包未加载时注册命令时没有 label/category 的场景
542
+ * 使用方需要自定处理 i18n 问题
543
+ */
544
+ getRawCommand(id: string): Command | undefined {
545
+ const command = this._commands[id];
546
+ return command;
547
+ }
548
+
549
+ /**
550
+ * 通过命令 id 获取命令
551
+ * @param commandId 命令 id
552
+ */
553
+ getCommand(id: string): Command | undefined {
554
+ const command = this._commands[id];
555
+ return command
556
+ ? {
557
+ ...command,
558
+ label: replaceLocalizePlaceholder(command.label),
559
+ category: replaceLocalizePlaceholder(command.category),
560
+ }
561
+ : undefined;
562
+ }
563
+
564
+ /**
565
+ * 给命令添加销毁函数
566
+ * @param command 要添加销毁函数的命令
567
+ */
568
+ protected doRegisterCommand(command: Command): IDisposable {
569
+ this._commands[command.id] = command;
570
+ return {
571
+ dispose: () => {
572
+ delete this._commands[command.id];
573
+ },
574
+ };
575
+ }
576
+
577
+ beforeExecuteCommand(commandId: string, interceptorFunc: PreInterceptorFunction): IDisposable;
578
+ beforeExecuteCommand(interceptor: PreCommandInterceptor): IDisposable;
579
+ public beforeExecuteCommand(
580
+ interceptor: string | PreCommandInterceptor,
581
+ interceptorFunc?: PreInterceptorFunction,
582
+ ): IDisposable {
583
+ if (typeof interceptor === 'string') {
584
+ const commandInterceptor = this.preCommandInterceptorMap.get(interceptor);
585
+ if (commandInterceptor) {
586
+ interceptorFunc && commandInterceptor.push(interceptorFunc);
587
+ } else {
588
+ interceptorFunc && this.preCommandInterceptorMap.set(interceptor, [interceptorFunc]);
589
+ }
590
+ return {
591
+ dispose: () => {
592
+ const commandInterceptor = this.preCommandInterceptorMap.get(interceptor);
593
+ if (commandInterceptor && interceptorFunc) {
594
+ const index = commandInterceptor.indexOf(interceptorFunc);
595
+ if (index !== -1) {
596
+ commandInterceptor.splice(index, 1);
597
+ }
598
+ }
599
+ },
600
+ };
601
+ } else {
602
+ this.preCommandInterceptors.push(interceptor);
603
+ return {
604
+ dispose: () => {
605
+ const index = this.preCommandInterceptors.indexOf(interceptor);
606
+ if (index !== -1) {
607
+ this.preCommandInterceptors.splice(index, 1);
608
+ }
609
+ },
610
+ };
611
+ }
612
+ }
613
+
614
+ afterExecuteCommand(commandId: string, interceptorFunc: PostInterceptorFunction): IDisposable;
615
+ afterExecuteCommand(interceptor: PostCommandInterceptor): IDisposable;
616
+ public afterExecuteCommand(interceptor: string | PostCommandInterceptor, result?: PostInterceptorFunction) {
617
+ if (typeof interceptor === 'string') {
618
+ const commandInterceptor = this.postCommandInterceptor.get(interceptor);
619
+ if (commandInterceptor) {
620
+ result && commandInterceptor.push(result);
621
+ } else {
622
+ result && this.postCommandInterceptor.set(interceptor, [result]);
623
+ }
624
+ return {
625
+ dispose: () => {
626
+ const commandInterceptor = this.postCommandInterceptor.get(interceptor);
627
+ if (commandInterceptor && result) {
628
+ const index = commandInterceptor.indexOf(result);
629
+ if (index !== -1) {
630
+ commandInterceptor.splice(index, 1);
631
+ }
632
+ }
633
+ },
634
+ };
635
+ } else {
636
+ this.postCommandInterceptors.push(interceptor);
637
+ return {
638
+ dispose: () => {
639
+ const index = this.postCommandInterceptors.indexOf(interceptor);
640
+ if (index !== -1) {
641
+ this.postCommandInterceptors.splice(index, 1);
642
+ }
643
+ },
644
+ };
645
+ }
646
+ }
647
+
648
+ /**
649
+ * 获取最近使用的命令列表
650
+ */
651
+ getRecentCommands() {
652
+ return this._recent;
653
+ }
654
+
655
+ /**
656
+ * 获取最近使用的命令列表
657
+ */
658
+ setRecentCommands(commands: Command[]) {
659
+ commands.forEach((command: Command) => {
660
+ this.addRecentCommand(command);
661
+ });
662
+ return this._recent;
663
+ }
664
+
665
+ /**
666
+ * 添加一个命令到最近使用列表中
667
+ * @param recent 待添加的命令
668
+ */
669
+ protected addRecentCommand(recent: Command): void {
670
+ if (!recent || !recent.label) {
671
+ return; // 某些情况会报错
672
+ }
673
+ // 确定命令当前是否存在于最近使用的列表中
674
+ const index = this._recent.findIndex((command) => command.id === recent.id);
675
+ // 如果已经存在,则从最近使用的列表中删除
676
+ if (index >= 0) {
677
+ this._recent.splice(index, 1);
678
+ }
679
+ // 将这个命令添加到最近使用的列表的第一位
680
+ this._recent.unshift(recent);
681
+ }
682
+ }
683
+
684
+ @Injectable()
685
+ export class CommandRegistryImpl extends CoreCommandRegistryImpl implements CommandRegistry {
686
+ @Autowired(CommandContribution)
687
+ protected readonly contributionProvider: ContributionProvider<CommandContribution>;
688
+
689
+ /**
690
+ * 执行 CommandContribution 的注册方法
691
+ */
692
+ initialize(): void {
693
+ const contributions = this.contributionProvider.getContributions();
694
+ for (const contrib of contributions) {
695
+ contrib.registerCommands(this);
696
+ }
697
+ }
698
+ }
699
+
700
+ @Injectable()
701
+ export class CommandServiceImpl implements CommandService {
702
+ @Autowired(CommandRegistry)
703
+ private commandRegistry: CommandRegistryImpl;
704
+
705
+ private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
706
+ readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
707
+ private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
708
+ readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;
709
+
710
+ executeCommand<T>(commandId: string, ...args: any[]): Promise<T | undefined> {
711
+ this._onWillExecuteCommand.fire({ commandId, args });
712
+ const result = this.commandRegistry.executeCommand<T>(commandId, ...args).finally(() => {
713
+ this._onDidExecuteCommand.fire({ commandId, args });
714
+ });
715
+ return Promise.resolve(result);
716
+ }
717
+
718
+ async tryExecuteCommand<T>(commandId: string, ...args: any[]): Promise<T | undefined> {
719
+ try {
720
+ return await this.executeCommand(commandId, ...args);
721
+ } catch (err) {
722
+ // no-op: failed when command not found
723
+ getDebugLogger().warn(err);
724
+ }
725
+ }
726
+ }