@theia/core 1.28.0-next.7 → 1.28.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 (206) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +33 -2
  3. package/i18n/nls.de.json +33 -2
  4. package/i18n/nls.es.json +33 -2
  5. package/i18n/nls.fr.json +33 -2
  6. package/i18n/nls.hu.json +33 -2
  7. package/i18n/nls.it.json +33 -2
  8. package/i18n/nls.ja.json +33 -2
  9. package/i18n/nls.json +35 -4
  10. package/i18n/nls.pl.json +33 -2
  11. package/i18n/nls.pt-br.json +33 -2
  12. package/i18n/nls.pt-pt.json +33 -2
  13. package/i18n/nls.ru.json +33 -2
  14. package/i18n/nls.zh-cn.json +33 -2
  15. package/lib/browser/common-frontend-contribution.d.ts +1 -1
  16. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  17. package/lib/browser/common-frontend-contribution.js +6 -5
  18. package/lib/browser/common-frontend-contribution.js.map +1 -1
  19. package/lib/browser/context-key-service.d.ts +3 -1
  20. package/lib/browser/context-key-service.d.ts.map +1 -1
  21. package/lib/browser/context-menu-renderer.d.ts +5 -0
  22. package/lib/browser/context-menu-renderer.d.ts.map +1 -1
  23. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  24. package/lib/browser/frontend-application-module.js +2 -0
  25. package/lib/browser/frontend-application-module.js.map +1 -1
  26. package/lib/browser/frontend-application.d.ts +3 -2
  27. package/lib/browser/frontend-application.d.ts.map +1 -1
  28. package/lib/browser/frontend-application.js.map +1 -1
  29. package/lib/browser/keybinding.d.ts.map +1 -1
  30. package/lib/browser/keybinding.js +9 -7
  31. package/lib/browser/keybinding.js.map +1 -1
  32. package/lib/browser/keybinding.spec.js +13 -3
  33. package/lib/browser/keybinding.spec.js.map +1 -1
  34. package/lib/browser/menu/browser-context-menu-renderer.d.ts +1 -1
  35. package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
  36. package/lib/browser/menu/browser-context-menu-renderer.js +2 -2
  37. package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
  38. package/lib/browser/menu/browser-menu-plugin.d.ts +23 -24
  39. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  40. package/lib/browser/menu/browser-menu-plugin.js +62 -79
  41. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  42. package/lib/browser/resource-context-key.d.ts +1 -0
  43. package/lib/browser/resource-context-key.d.ts.map +1 -1
  44. package/lib/browser/resource-context-key.js +8 -6
  45. package/lib/browser/resource-context-key.js.map +1 -1
  46. package/lib/browser/shell/shell-layout-restorer.d.ts.map +1 -1
  47. package/lib/browser/shell/shell-layout-restorer.js +2 -1
  48. package/lib/browser/shell/shell-layout-restorer.js.map +1 -1
  49. package/lib/browser/shell/side-panel-toolbar.d.ts.map +1 -1
  50. package/lib/browser/shell/side-panel-toolbar.js +0 -1
  51. package/lib/browser/shell/side-panel-toolbar.js.map +1 -1
  52. package/lib/browser/shell/tab-bar-toolbar/index.d.ts +4 -0
  53. package/lib/browser/shell/tab-bar-toolbar/index.d.ts.map +1 -0
  54. package/lib/browser/shell/tab-bar-toolbar/index.js +31 -0
  55. package/lib/browser/shell/tab-bar-toolbar/index.js.map +1 -0
  56. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts +16 -0
  57. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -0
  58. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +36 -0
  59. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -0
  60. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +51 -0
  61. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -0
  62. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +173 -0
  63. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -0
  64. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts +119 -0
  65. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts.map +1 -0
  66. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js +67 -0
  67. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js.map +1 -0
  68. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +53 -0
  69. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -0
  70. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +264 -0
  71. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -0
  72. package/lib/browser/shell/{tab-bar-toolbar.spec.d.ts → tab-bar-toolbar/tab-bar-toolbar.spec.d.ts} +0 -0
  73. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.d.ts.map +1 -0
  74. package/lib/browser/shell/{tab-bar-toolbar.spec.js → tab-bar-toolbar/tab-bar-toolbar.spec.js} +3 -3
  75. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -0
  76. package/lib/browser/view-container.d.ts.map +1 -1
  77. package/lib/browser/view-container.js +1 -10
  78. package/lib/browser/view-container.js.map +1 -1
  79. package/lib/browser/widget-manager.d.ts.map +1 -1
  80. package/lib/browser/widget-manager.js +2 -1
  81. package/lib/browser/widget-manager.js.map +1 -1
  82. package/lib/browser/widget-manager.spec.js +13 -0
  83. package/lib/browser/widget-manager.spec.js.map +1 -1
  84. package/lib/browser/window/default-window-service.d.ts +2 -1
  85. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  86. package/lib/browser/window/default-window-service.js +9 -4
  87. package/lib/browser/window/default-window-service.js.map +1 -1
  88. package/lib/browser/window/window-service.d.ts +2 -1
  89. package/lib/browser/window/window-service.d.ts.map +1 -1
  90. package/lib/browser/window/window-service.js.map +1 -1
  91. package/lib/common/color.d.ts +3 -0
  92. package/lib/common/color.d.ts.map +1 -1
  93. package/lib/common/frontend-application-state.d.ts +14 -0
  94. package/lib/common/frontend-application-state.d.ts.map +1 -1
  95. package/lib/common/frontend-application-state.js +16 -0
  96. package/lib/common/frontend-application-state.js.map +1 -1
  97. package/lib/common/menu/action-menu-node.d.ts +20 -0
  98. package/lib/common/menu/action-menu-node.d.ts.map +1 -0
  99. package/lib/common/menu/action-menu-node.js +57 -0
  100. package/lib/common/menu/action-menu-node.js.map +1 -0
  101. package/lib/common/menu/composite-menu-node.d.ts +52 -0
  102. package/lib/common/menu/composite-menu-node.d.ts.map +1 -0
  103. package/lib/common/menu/composite-menu-node.js +102 -0
  104. package/lib/common/menu/composite-menu-node.js.map +1 -0
  105. package/lib/common/menu/index.d.ts +6 -0
  106. package/lib/common/menu/index.d.ts.map +1 -0
  107. package/lib/common/menu/index.js +33 -0
  108. package/lib/common/menu/index.js.map +1 -0
  109. package/lib/common/menu/menu-adapter.d.ts +36 -0
  110. package/lib/common/menu/menu-adapter.d.ts.map +1 -0
  111. package/lib/common/menu/menu-adapter.js +101 -0
  112. package/lib/common/menu/menu-adapter.js.map +1 -0
  113. package/lib/common/{menu.d.ts → menu/menu-model-registry.d.ts} +22 -124
  114. package/lib/common/menu/menu-model-registry.d.ts.map +1 -0
  115. package/lib/common/{menu.js → menu/menu-model-registry.js} +71 -147
  116. package/lib/common/menu/menu-model-registry.js.map +1 -0
  117. package/lib/common/menu/menu-types.d.ts +120 -0
  118. package/lib/common/menu/menu-types.d.ts.map +1 -0
  119. package/lib/common/menu/menu-types.js +84 -0
  120. package/lib/common/menu/menu-types.js.map +1 -0
  121. package/lib/common/{menu.spec.d.ts → menu/menu.spec.d.ts} +0 -0
  122. package/lib/common/menu/menu.spec.d.ts.map +1 -0
  123. package/lib/common/{menu.spec.js → menu/menu.spec.js} +3 -3
  124. package/lib/common/menu/menu.spec.js.map +1 -0
  125. package/lib/common/message-rpc/rpc-message-encoder.d.ts +12 -11
  126. package/lib/common/message-rpc/rpc-message-encoder.d.ts.map +1 -1
  127. package/lib/common/message-rpc/rpc-message-encoder.js +21 -12
  128. package/lib/common/message-rpc/rpc-message-encoder.js.map +1 -1
  129. package/lib/common/messaging/abstract-connection-provider.d.ts +1 -1
  130. package/lib/common/messaging/abstract-connection-provider.js +3 -3
  131. package/lib/common/quick-pick-service.d.ts +1 -0
  132. package/lib/common/quick-pick-service.d.ts.map +1 -1
  133. package/lib/common/quick-pick-service.js.map +1 -1
  134. package/lib/electron-browser/menu/electron-context-menu-renderer.js +2 -2
  135. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  136. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +14 -5
  137. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  138. package/lib/electron-browser/menu/electron-main-menu-factory.js +71 -82
  139. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  140. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  141. package/lib/electron-browser/menu/electron-menu-contribution.js +6 -0
  142. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  143. package/lib/electron-browser/messaging/electron-ws-connection-provider.js +1 -1
  144. package/lib/electron-browser/window/electron-window-service.js +1 -1
  145. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  146. package/lib/electron-common/messaging/electron-messages.d.ts +3 -14
  147. package/lib/electron-common/messaging/electron-messages.d.ts.map +1 -1
  148. package/lib/electron-common/messaging/electron-messages.js +3 -16
  149. package/lib/electron-common/messaging/electron-messages.js.map +1 -1
  150. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +3 -3
  151. package/lib/electron-main/messaging/electron-messaging-contribution.js +6 -6
  152. package/package.json +7 -6
  153. package/src/browser/common-frontend-contribution.ts +6 -5
  154. package/src/browser/context-key-service.ts +1 -1
  155. package/src/browser/context-menu-renderer.ts +5 -0
  156. package/src/browser/frontend-application-module.ts +7 -1
  157. package/src/browser/frontend-application.ts +3 -2
  158. package/src/browser/keybinding.spec.ts +13 -3
  159. package/src/browser/keybinding.ts +8 -9
  160. package/src/browser/menu/browser-context-menu-renderer.ts +2 -2
  161. package/src/browser/menu/browser-menu-plugin.ts +74 -86
  162. package/src/browser/resource-context-key.ts +9 -6
  163. package/src/browser/shell/shell-layout-restorer.ts +2 -1
  164. package/src/browser/shell/side-panel-toolbar.ts +0 -1
  165. package/src/browser/shell/tab-bar-toolbar/index.ts +19 -0
  166. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts +31 -0
  167. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +170 -0
  168. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +186 -0
  169. package/src/browser/shell/{tab-bar-toolbar.spec.ts → tab-bar-toolbar/tab-bar-toolbar.spec.ts} +2 -2
  170. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +261 -0
  171. package/src/browser/style/alert-messages.css +6 -0
  172. package/src/browser/view-container.ts +1 -10
  173. package/src/browser/widget-manager.spec.ts +14 -0
  174. package/src/browser/widget-manager.ts +2 -1
  175. package/src/browser/window/default-window-service.ts +9 -4
  176. package/src/browser/window/window-service.ts +2 -1
  177. package/src/common/color.ts +3 -0
  178. package/src/common/frontend-application-state.ts +15 -0
  179. package/src/common/menu/action-menu-node.ts +65 -0
  180. package/src/common/menu/composite-menu-node.ts +121 -0
  181. package/src/common/menu/index.ts +21 -0
  182. package/src/common/menu/menu-adapter.ts +103 -0
  183. package/src/common/{menu.ts → menu/menu-model-registry.ts} +61 -222
  184. package/src/common/menu/menu-types.ts +183 -0
  185. package/src/common/{menu.spec.ts → menu/menu.spec.ts} +3 -2
  186. package/src/common/message-rpc/rpc-message-encoder.ts +24 -13
  187. package/src/common/messaging/abstract-connection-provider.ts +4 -4
  188. package/src/common/quick-pick-service.ts +1 -0
  189. package/src/electron-browser/menu/electron-context-menu-renderer.ts +2 -2
  190. package/src/electron-browser/menu/electron-main-menu-factory.ts +82 -96
  191. package/src/electron-browser/menu/electron-menu-contribution.ts +8 -0
  192. package/src/electron-browser/messaging/electron-ws-connection-provider.ts +1 -1
  193. package/src/electron-browser/window/electron-window-service.ts +1 -1
  194. package/src/electron-common/messaging/electron-messages.ts +4 -15
  195. package/src/electron-main/messaging/electron-messaging-contribution.ts +8 -8
  196. package/lib/browser/shell/tab-bar-toolbar.d.ts +0 -186
  197. package/lib/browser/shell/tab-bar-toolbar.d.ts.map +0 -1
  198. package/lib/browser/shell/tab-bar-toolbar.js +0 -362
  199. package/lib/browser/shell/tab-bar-toolbar.js.map +0 -1
  200. package/lib/browser/shell/tab-bar-toolbar.spec.d.ts.map +0 -1
  201. package/lib/browser/shell/tab-bar-toolbar.spec.js.map +0 -1
  202. package/lib/common/menu.d.ts.map +0 -1
  203. package/lib/common/menu.js.map +0 -1
  204. package/lib/common/menu.spec.d.ts.map +0 -1
  205. package/lib/common/menu.spec.js.map +0 -1
  206. package/src/browser/shell/tab-bar-toolbar.tsx +0 -495
@@ -0,0 +1,65 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Ericsson and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { CommandRegistry } from '../command';
18
+ import { AlternativeHandlerMenuNode, CommandMenuNode, MenuAction, MenuNode } from './menu-types';
19
+
20
+ /**
21
+ * Node representing an action in the menu tree structure.
22
+ * It's based on {@link MenuAction} for which it tries to determine the
23
+ * best label, icon and sortString with the given data.
24
+ */
25
+ export class ActionMenuNode implements MenuNode, CommandMenuNode, Partial<AlternativeHandlerMenuNode> {
26
+
27
+ readonly altNode: ActionMenuNode | undefined;
28
+
29
+ constructor(
30
+ protected readonly action: MenuAction,
31
+ protected readonly commands: CommandRegistry,
32
+ ) {
33
+ if (action.alt) {
34
+ this.altNode = new ActionMenuNode({ commandId: action.alt }, commands);
35
+ }
36
+ }
37
+
38
+ get command(): string { return this.action.commandId; };
39
+
40
+ get when(): string | undefined { return this.action.when; }
41
+
42
+ get id(): string { return this.action.commandId; }
43
+
44
+ get label(): string {
45
+ if (this.action.label) {
46
+ return this.action.label;
47
+ }
48
+ const cmd = this.commands.getCommand(this.action.commandId);
49
+ if (!cmd) {
50
+ console.debug(`No label for action menu node: No command "${this.action.commandId}" exists.`);
51
+ return '';
52
+ }
53
+ return cmd.label || cmd.id;
54
+ }
55
+
56
+ get icon(): string | undefined {
57
+ if (this.action.icon) {
58
+ return this.action.icon;
59
+ }
60
+ const command = this.commands.getCommand(this.action.commandId);
61
+ return command && command.iconClass;
62
+ }
63
+
64
+ get sortString(): string { return this.action.order || this.label; }
65
+ }
@@ -0,0 +1,121 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Ericsson and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Disposable } from '../disposable';
18
+ import { CompoundMenuNode, CompoundMenuNodeMetadata, CompoundMenuNodeRole, MenuNode, SubMenuOptions } from './menu-types';
19
+
20
+ /**
21
+ * Node representing a (sub)menu in the menu tree structure.
22
+ */
23
+ export class CompositeMenuNode implements MenuNode, CompoundMenuNode, CompoundMenuNodeMetadata {
24
+ protected readonly _children: MenuNode[] = [];
25
+ public iconClass?: string;
26
+ public order?: string;
27
+ readonly when?: string;
28
+ readonly _role?: CompoundMenuNodeRole;
29
+
30
+ constructor(
31
+ public readonly id: string,
32
+ public label?: string,
33
+ options?: SubMenuOptions,
34
+ readonly parent?: MenuNode & CompoundMenuNode,
35
+ ) {
36
+ if (options) {
37
+ this.iconClass = options.iconClass;
38
+ this.order = options.order;
39
+ this.when = options.when;
40
+ this._role = options?.role;
41
+ }
42
+ }
43
+
44
+ get icon(): string | undefined {
45
+ return this.iconClass;
46
+ }
47
+
48
+ get children(): ReadonlyArray<MenuNode> {
49
+ return this._children;
50
+ }
51
+
52
+ get role(): CompoundMenuNodeRole { return this._role ?? (this.label ? CompoundMenuNodeRole.Submenu : CompoundMenuNodeRole.Group); }
53
+
54
+ /**
55
+ * Inserts the given node at the position indicated by `sortString`.
56
+ *
57
+ * @returns a disposable which, when called, will remove the given node again.
58
+ */
59
+ public addNode(node: MenuNode): Disposable {
60
+ this._children.push(node);
61
+ this._children.sort(CompoundMenuNode.sortChildren);
62
+ return {
63
+ dispose: () => {
64
+ const idx = this._children.indexOf(node);
65
+ if (idx >= 0) {
66
+ this._children.splice(idx, 1);
67
+ }
68
+ }
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Removes the first node with the given id.
74
+ *
75
+ * @param id node id.
76
+ */
77
+ public removeNode(id: string): void {
78
+ const node = this._children.find(n => n.id === id);
79
+ if (node) {
80
+ const idx = this._children.indexOf(node);
81
+ if (idx >= 0) {
82
+ this._children.splice(idx, 1);
83
+ }
84
+ }
85
+ }
86
+
87
+ get sortString(): string {
88
+ return this.order || this.id;
89
+ }
90
+
91
+ get isSubmenu(): boolean {
92
+ return Boolean(this.label);
93
+ }
94
+
95
+ /** @deprecated @since 1.28 use CompoundMenuNode.isNavigationGroup instead */
96
+ static isNavigationGroup = CompoundMenuNode.isNavigationGroup;
97
+ }
98
+
99
+ export class CompositeMenuNodeWrapper implements MenuNode, CompoundMenuNodeMetadata {
100
+ constructor(protected readonly wrapped: Readonly<CompositeMenuNode>, readonly parent: MenuNode & CompoundMenuNode, protected readonly options?: SubMenuOptions) { }
101
+
102
+ get id(): string { return this.wrapped.id; }
103
+
104
+ get label(): string | undefined { return this.wrapped.label; }
105
+
106
+ get sortString(): string { return this.order || this.id; }
107
+
108
+ get isSubmenu(): boolean { return Boolean(this.label); }
109
+
110
+ get role(): CompoundMenuNodeRole { return this.options?.role ?? this.wrapped.role; }
111
+
112
+ get icon(): string | undefined { return this.iconClass; }
113
+
114
+ get iconClass(): string | undefined { return this.options?.iconClass ?? this.wrapped.iconClass; }
115
+
116
+ get order(): string | undefined { return this.options?.order ?? this.wrapped.order; }
117
+
118
+ get when(): string | undefined { return this.options?.when ?? this.wrapped.when; }
119
+
120
+ get children(): ReadonlyArray<MenuNode> { return this.wrapped.children; }
121
+ }
@@ -0,0 +1,21 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Ericsson and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ export * from './action-menu-node';
18
+ export * from './composite-menu-node';
19
+ export * from './menu-adapter';
20
+ export * from './menu-model-registry';
21
+ export * from './menu-types';
@@ -0,0 +1,103 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Ericsson and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable } from 'inversify';
18
+ import { CommandRegistry } from '../command';
19
+ import { Disposable } from '../disposable';
20
+ import { MenuPath } from './menu-types';
21
+
22
+ export type MenuCommandArguments = [menuPath: MenuPath, command: string, ...commandArgs: unknown[]];
23
+
24
+ export const MenuCommandExecutor = Symbol('MenuCommandExecutor');
25
+ export interface MenuCommandExecutor {
26
+ isVisible(...args: MenuCommandArguments): boolean;
27
+ isEnabled(...args: MenuCommandArguments): boolean;
28
+ isToggled(...args: MenuCommandArguments): boolean;
29
+ executeCommand(...args: MenuCommandArguments): Promise<unknown>;
30
+ };
31
+
32
+ export const MenuCommandAdapter = Symbol('MenuCommandAdapter');
33
+ export interface MenuCommandAdapter extends MenuCommandExecutor {
34
+ /** Return values less than or equal to 0 are treated as rejections. */
35
+ canHandle(...args: MenuCommandArguments): number;
36
+ }
37
+
38
+ export const MenuCommandAdapterRegistry = Symbol('MenuCommandAdapterRegistry');
39
+ export interface MenuCommandAdapterRegistry {
40
+ registerAdapter(adapter: MenuCommandAdapter): Disposable;
41
+ getAdapterFor(...args: MenuCommandArguments): MenuCommandAdapter | undefined;
42
+ }
43
+
44
+ @injectable()
45
+ export class MenuCommandExecutorImpl implements MenuCommandExecutor {
46
+ @inject(MenuCommandAdapterRegistry) protected readonly adapterRegistry: MenuCommandAdapterRegistry;
47
+ @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
48
+
49
+ executeCommand(menuPath: MenuPath, command: string, ...commandArgs: unknown[]): Promise<unknown> {
50
+ return this.delegate(menuPath, command, commandArgs, 'executeCommand');
51
+ }
52
+
53
+ isVisible(menuPath: MenuPath, command: string, ...commandArgs: unknown[]): boolean {
54
+ return this.delegate(menuPath, command, commandArgs, 'isVisible');
55
+ }
56
+
57
+ isEnabled(menuPath: MenuPath, command: string, ...commandArgs: unknown[]): boolean {
58
+ return this.delegate(menuPath, command, commandArgs, 'isEnabled');
59
+ }
60
+
61
+ isToggled(menuPath: MenuPath, command: string, ...commandArgs: unknown[]): boolean {
62
+ return this.delegate(menuPath, command, commandArgs, 'isToggled');
63
+ }
64
+
65
+ protected delegate<T extends keyof MenuCommandExecutor>(menuPath: MenuPath, command: string, commandArgs: unknown[], method: T): ReturnType<MenuCommandExecutor[T]> {
66
+ const adapter = this.adapterRegistry.getAdapterFor(menuPath, command, commandArgs);
67
+ return (adapter
68
+ ? adapter[method](menuPath, command, ...commandArgs)
69
+ : this.commandRegistry[method](command, ...commandArgs)) as ReturnType<MenuCommandExecutor[T]>;
70
+ }
71
+ }
72
+
73
+ @injectable()
74
+ export class MenuCommandAdapterRegistryImpl implements MenuCommandAdapterRegistry {
75
+ protected readonly adapters = new Array<MenuCommandAdapter>();
76
+
77
+ registerAdapter(adapter: MenuCommandAdapter): Disposable {
78
+ if (!this.adapters.includes(adapter)) {
79
+ this.adapters.push(adapter);
80
+ return Disposable.create(() => {
81
+ const index = this.adapters.indexOf(adapter);
82
+ if (index !== -1) {
83
+ this.adapters.splice(index, 1);
84
+ }
85
+ });
86
+ }
87
+ return Disposable.NULL;
88
+ }
89
+
90
+ getAdapterFor(menuPath: MenuPath, command: string, ...commandArgs: unknown[]): MenuCommandAdapter | undefined {
91
+ let bestAdapter: MenuCommandAdapter | undefined = undefined;
92
+ let bestScore = 0;
93
+ let currentScore = 0;
94
+ for (const adapter of this.adapters) {
95
+ // Greater than or equal: favor later registrations over earlier.
96
+ if ((currentScore = adapter.canHandle(menuPath, command, ...commandArgs)) >= bestScore) {
97
+ bestScore = currentScore;
98
+ bestAdapter = adapter;
99
+ }
100
+ }
101
+ return bestAdapter;
102
+ }
103
+ }
@@ -15,74 +15,12 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable, inject, named } from 'inversify';
18
- import { Disposable } from './disposable';
19
- import { CommandRegistry, Command } from './command';
20
- import { ContributionProvider } from './contribution-provider';
21
-
22
- /**
23
- * A menu entry representing an action, e.g. "New File".
24
- */
25
- export interface MenuAction {
26
- /**
27
- * The command to execute.
28
- */
29
- commandId: string
30
- /**
31
- * In addition to the mandatory command property, an alternative command can be defined.
32
- * It will be shown and invoked when pressing Alt while opening a menu.
33
- */
34
- alt?: string;
35
- /**
36
- * A specific label for this action. If not specified the command label or command id will be used.
37
- */
38
- label?: string
39
- /**
40
- * Icon class(es). If not specified the icon class associated with the specified command
41
- * (i.e. `command.iconClass`) will be used if it exists.
42
- */
43
- icon?: string
44
- /**
45
- * Menu entries are sorted in ascending order based on their `order` strings. If omitted the determined
46
- * label will be used instead.
47
- */
48
- order?: string
49
- /**
50
- * Optional expression which will be evaluated by the {@link ContextKeyService} to determine visibility
51
- * of the action, e.g. `resourceLangId == markdown`.
52
- */
53
- when?: string
54
- }
55
-
56
- export namespace MenuAction {
57
- /* Determine whether object is a MenuAction */
58
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
- export function is(arg: MenuAction | any): arg is MenuAction {
60
- return !!arg && arg === Object(arg) && 'commandId' in arg;
61
- }
62
- }
63
-
64
- /**
65
- * Additional options when creating a new submenu.
66
- */
67
- export interface SubMenuOptions {
68
- /**
69
- * The class to use for the submenu icon.
70
- */
71
- iconClass?: string
72
- /**
73
- * Menu entries are sorted in ascending order based on their `order` strings. If omitted the determined
74
- * label will be used instead.
75
- */
76
- order?: string
77
- }
78
-
79
- export type MenuPath = string[];
80
-
81
- export const MAIN_MENU_BAR: MenuPath = ['menubar'];
82
-
83
- export const SETTINGS_MENU: MenuPath = ['settings_menu'];
84
- export const ACCOUNTS_MENU: MenuPath = ['accounts_menu'];
85
- export const ACCOUNTS_SUBMENU = [...ACCOUNTS_MENU, '1_accounts_submenu'];
18
+ import { Disposable } from '../disposable';
19
+ import { CommandRegistry, Command } from '../command';
20
+ import { ContributionProvider } from '../contribution-provider';
21
+ import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node';
22
+ import { MenuAction, MenuNode, MenuPath, SubMenuOptions } from './menu-types';
23
+ import { ActionMenuNode } from './action-menu-node';
86
24
 
87
25
  export const MenuContribution = Symbol('MenuContribution');
88
26
 
@@ -128,6 +66,7 @@ export interface MenuContribution {
128
66
  @injectable()
129
67
  export class MenuModelRegistry {
130
68
  protected readonly root = new CompositeMenuNode('');
69
+ protected readonly independentSubmenus = new Map<string, CompositeMenuNode>();
131
70
 
132
71
  constructor(
133
72
  @inject(ContributionProvider) @named(MenuContribution)
@@ -156,11 +95,24 @@ export class MenuModelRegistry {
156
95
  *
157
96
  * @returns a disposable which, when called, will remove the menu node again.
158
97
  */
159
- registerMenuNode(menuPath: MenuPath, menuNode: MenuNode): Disposable {
160
- const parent = this.findGroup(menuPath);
98
+ registerMenuNode(menuPath: MenuPath | string, menuNode: MenuNode, group?: string): Disposable {
99
+ const parent = this.getMenuNode(menuPath, group);
161
100
  return parent.addNode(menuNode);
162
101
  }
163
102
 
103
+ getMenuNode(menuPath: MenuPath | string, group?: string): CompositeMenuNode {
104
+ if (typeof menuPath === 'string') {
105
+ const target = this.independentSubmenus.get(menuPath);
106
+ if (!target) { throw new Error(`Could not find submenu with id ${menuPath}`); }
107
+ if (group) {
108
+ return this.findSubMenu(target, group);
109
+ }
110
+ return target;
111
+ } else {
112
+ return this.findGroup(group ? menuPath.concat(group) : menuPath);
113
+ }
114
+ }
115
+
164
116
  /**
165
117
  * Register a new menu at the given path with the given label.
166
118
  * (If the menu already exists without a label, iconClass or order this method can be used to set them.)
@@ -186,7 +138,7 @@ export class MenuModelRegistry {
186
138
  const parent = this.findGroup(groupPath, options);
187
139
  let groupNode = this.findSubMenu(parent, menuId, options);
188
140
  if (!groupNode) {
189
- groupNode = new CompositeMenuNode(menuId, label, options);
141
+ groupNode = new CompositeMenuNode(menuId, label, options, parent);
190
142
  return parent.addNode(groupNode);
191
143
  } else {
192
144
  if (!groupNode.label) {
@@ -206,6 +158,21 @@ export class MenuModelRegistry {
206
158
  }
207
159
  }
208
160
 
161
+ registerIndependentSubmenu(id: string, label: string, options?: SubMenuOptions): Disposable {
162
+ if (this.independentSubmenus.has(id)) {
163
+ console.debug(`Independent submenu with path ${id} registered, but given ID already exists.`);
164
+ }
165
+ this.independentSubmenus.set(id, new CompositeMenuNode(id, label, options));
166
+ return { dispose: () => this.independentSubmenus.delete(id) };
167
+ }
168
+
169
+ linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable {
170
+ const child = this.getMenuNode(childId);
171
+ const parent = this.getMenuNode(parentPath, group);
172
+ const wrapper = new CompositeMenuNodeWrapper(child, parent, options);
173
+ return parent.addNode(wrapper);
174
+ }
175
+
209
176
  /**
210
177
  * Unregister all menu nodes with the same id as the given menu action.
211
178
  *
@@ -258,6 +225,10 @@ export class MenuModelRegistry {
258
225
  recurse(this.root);
259
226
  }
260
227
 
228
+ /**
229
+ * Finds a submenu as a descendant of the `root` node.
230
+ * See {@link MenuModelRegistry.findSubMenu findSubMenu}.
231
+ */
261
232
  protected findGroup(menuPath: MenuPath, options?: SubMenuOptions): CompositeMenuNode {
262
233
  let currentMenu = this.root;
263
234
  for (const segment of menuPath) {
@@ -266,6 +237,10 @@ export class MenuModelRegistry {
266
237
  return currentMenu;
267
238
  }
268
239
 
240
+ /**
241
+ * Finds or creates a submenu as an immediate child of `current`.
242
+ * @throws if a node with the given `menuId` exists but is not a {@link CompositeMenuNode}.
243
+ */
269
244
  protected findSubMenu(current: CompositeMenuNode, menuId: string, options?: SubMenuOptions): CompositeMenuNode {
270
245
  const sub = current.children.find(e => e.id === menuId);
271
246
  if (sub instanceof CompositeMenuNode) {
@@ -274,7 +249,7 @@ export class MenuModelRegistry {
274
249
  if (sub) {
275
250
  throw new Error(`'${menuId}' is not a menu group.`);
276
251
  }
277
- const newSub = new CompositeMenuNode(menuId, undefined, options);
252
+ const newSub = new CompositeMenuNode(menuId, undefined, options, current);
278
253
  current.addNode(newSub);
279
254
  return newSub;
280
255
  }
@@ -290,160 +265,24 @@ export class MenuModelRegistry {
290
265
  getMenu(menuPath: MenuPath = []): CompositeMenuNode {
291
266
  return this.findGroup(menuPath);
292
267
  }
293
- }
294
268
 
295
- /**
296
- * Base interface of the nodes used in the menu tree structure.
297
- */
298
- export interface MenuNode {
299
269
  /**
300
- * the optional label for this specific node.
270
+ * Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined.
271
+ * Returns `undefined` if the `parent` of any node in the chain is unknown.
301
272
  */
302
- readonly label?: string
303
- /**
304
- * technical identifier.
305
- */
306
- readonly id: string
307
- /**
308
- * Menu nodes are sorted in ascending order based on their `sortString`.
309
- */
310
- readonly sortString: string
311
- }
312
-
313
- /**
314
- * Node representing a (sub)menu in the menu tree structure.
315
- */
316
- export class CompositeMenuNode implements MenuNode {
317
- protected readonly _children: MenuNode[] = [];
318
- public iconClass?: string;
319
- public order?: string;
320
-
321
- constructor(
322
- public readonly id: string,
323
- public label?: string,
324
- options?: SubMenuOptions
325
- ) {
326
- if (options) {
327
- this.iconClass = options.iconClass;
328
- this.order = options.order;
329
- }
330
- }
331
-
332
- get children(): ReadonlyArray<MenuNode> {
333
- return this._children;
334
- }
335
-
336
- /**
337
- * Inserts the given node at the position indicated by `sortString`.
338
- *
339
- * @returns a disposable which, when called, will remove the given node again.
340
- */
341
- public addNode(node: MenuNode): Disposable {
342
- this._children.push(node);
343
- this._children.sort((m1, m2) => {
344
- // The navigation group is special as it will always be sorted to the top/beginning of a menu.
345
- if (CompositeMenuNode.isNavigationGroup(m1)) {
346
- return -1;
347
- }
348
- if (CompositeMenuNode.isNavigationGroup(m2)) {
349
- return 1;
273
+ getPath(node: MenuNode): MenuPath | undefined {
274
+ const identifiers = [];
275
+ const visited: MenuNode[] = [];
276
+ let next: MenuNode | undefined = node;
277
+
278
+ while (next && !visited.includes(next)) {
279
+ if (next === this.root) {
280
+ return identifiers.reverse();
350
281
  }
351
- if (m1.sortString < m2.sortString) {
352
- return -1;
353
- } else if (m1.sortString > m2.sortString) {
354
- return 1;
355
- } else {
356
- return 0;
357
- }
358
- });
359
- return {
360
- dispose: () => {
361
- const idx = this._children.indexOf(node);
362
- if (idx >= 0) {
363
- this._children.splice(idx, 1);
364
- }
365
- }
366
- };
367
- }
368
-
369
- /**
370
- * Removes the first node with the given id.
371
- *
372
- * @param id node id.
373
- */
374
- public removeNode(id: string): void {
375
- const node = this._children.find(n => n.id === id);
376
- if (node) {
377
- const idx = this._children.indexOf(node);
378
- if (idx >= 0) {
379
- this._children.splice(idx, 1);
380
- }
381
- }
382
- }
383
-
384
- get sortString(): string {
385
- return this.order || this.id;
386
- }
387
-
388
- get isSubmenu(): boolean {
389
- return this.label !== undefined;
390
- }
391
-
392
- /**
393
- * Indicates whether the given node is the special `navigation` menu.
394
- *
395
- * @param node the menu node to check.
396
- * @returns `true` when the given node is a {@link CompositeMenuNode} with id `navigation`,
397
- * `false` otherwise.
398
- */
399
- static isNavigationGroup(node: MenuNode): node is CompositeMenuNode {
400
- return node instanceof CompositeMenuNode && node.id === 'navigation';
401
- }
402
- }
403
-
404
- /**
405
- * Node representing an action in the menu tree structure.
406
- * It's based on {@link MenuAction} for which it tries to determine the
407
- * best label, icon and sortString with the given data.
408
- */
409
- export class ActionMenuNode implements MenuNode {
410
-
411
- readonly altNode: ActionMenuNode | undefined;
412
-
413
- constructor(
414
- public readonly action: MenuAction,
415
- protected readonly commands: CommandRegistry
416
- ) {
417
- if (action.alt) {
418
- this.altNode = new ActionMenuNode({ commandId: action.alt }, commands);
419
- }
420
- }
421
-
422
- get id(): string {
423
- return this.action.commandId;
424
- }
425
-
426
- get label(): string {
427
- if (this.action.label) {
428
- return this.action.label;
282
+ visited.push(next);
283
+ identifiers.push(next.id);
284
+ next = next.parent;
429
285
  }
430
- const cmd = this.commands.getCommand(this.action.commandId);
431
- if (!cmd) {
432
- console.debug(`No label for action menu node: No command "${this.action.commandId}" exists.`);
433
- return '';
434
- }
435
- return cmd.label || cmd.id;
436
- }
437
-
438
- get icon(): string | undefined {
439
- if (this.action.icon) {
440
- return this.action.icon;
441
- }
442
- const command = this.commands.getCommand(this.action.commandId);
443
- return command && command.iconClass;
444
- }
445
-
446
- get sortString(): string {
447
- return this.action.order || this.label;
286
+ return undefined;
448
287
  }
449
288
  }