@theia/core 1.61.0 → 1.62.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 (244) hide show
  1. package/README.md +7 -7
  2. package/i18n/nls.cs.json +158 -13
  3. package/i18n/nls.de.json +158 -13
  4. package/i18n/nls.es.json +158 -13
  5. package/i18n/nls.fr.json +158 -13
  6. package/i18n/nls.hu.json +158 -13
  7. package/i18n/nls.it.json +158 -13
  8. package/i18n/nls.ja.json +158 -13
  9. package/i18n/nls.json +159 -14
  10. package/i18n/nls.ko.json +158 -13
  11. package/i18n/nls.pl.json +158 -13
  12. package/i18n/nls.pt-br.json +158 -13
  13. package/i18n/nls.ru.json +158 -13
  14. package/i18n/nls.tr.json +158 -13
  15. package/i18n/nls.zh-cn.json +158 -13
  16. package/i18n/nls.zh-tw.json +158 -13
  17. package/lib/browser/catalog.json +197 -33
  18. package/lib/browser/common-frontend-contribution.d.ts +1 -1
  19. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  20. package/lib/browser/common-frontend-contribution.js +13 -11
  21. package/lib/browser/common-frontend-contribution.js.map +1 -1
  22. package/lib/browser/context-menu-renderer.d.ts +14 -3
  23. package/lib/browser/context-menu-renderer.d.ts.map +1 -1
  24. package/lib/browser/context-menu-renderer.js +23 -1
  25. package/lib/browser/context-menu-renderer.js.map +1 -1
  26. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  27. package/lib/browser/frontend-application-module.js +1 -3
  28. package/lib/browser/frontend-application-module.js.map +1 -1
  29. package/lib/browser/hover-service.d.ts.map +1 -1
  30. package/lib/browser/hover-service.js +7 -0
  31. package/lib/browser/hover-service.js.map +1 -1
  32. package/lib/browser/menu/action-menu-node.d.ts +36 -0
  33. package/lib/browser/menu/action-menu-node.d.ts.map +1 -0
  34. package/lib/browser/menu/action-menu-node.js +113 -0
  35. package/lib/browser/menu/action-menu-node.js.map +1 -0
  36. package/lib/browser/menu/browser-context-menu-renderer.d.ts +12 -4
  37. package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
  38. package/lib/browser/menu/browser-context-menu-renderer.js +12 -13
  39. package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
  40. package/lib/browser/menu/browser-menu-module.d.ts.map +1 -1
  41. package/lib/browser/menu/browser-menu-module.js +4 -0
  42. package/lib/browser/menu/browser-menu-module.js.map +1 -1
  43. package/lib/browser/menu/browser-menu-node-factory.d.ts +13 -0
  44. package/lib/browser/menu/browser-menu-node-factory.d.ts.map +1 -0
  45. package/lib/browser/menu/browser-menu-node-factory.js +54 -0
  46. package/lib/browser/menu/browser-menu-node-factory.js.map +1 -0
  47. package/lib/browser/menu/browser-menu-plugin.d.ts +12 -30
  48. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  49. package/lib/browser/menu/browser-menu-plugin.js +78 -159
  50. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  51. package/lib/browser/menu/composite-menu-node.d.ts +49 -0
  52. package/lib/browser/menu/composite-menu-node.d.ts.map +1 -0
  53. package/lib/browser/menu/composite-menu-node.js +127 -0
  54. package/lib/browser/menu/composite-menu-node.js.map +1 -0
  55. package/lib/browser/menu/menu.spec.d.ts.map +1 -0
  56. package/lib/{common → browser}/menu/menu.spec.js +38 -13
  57. package/lib/browser/menu/menu.spec.js.map +1 -0
  58. package/lib/browser/open-with-service.d.ts +1 -1
  59. package/lib/browser/saveable-service.d.ts.map +1 -1
  60. package/lib/browser/saveable-service.js +6 -1
  61. package/lib/browser/saveable-service.js.map +1 -1
  62. package/lib/browser/shell/application-shell.d.ts +7 -5
  63. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  64. package/lib/browser/shell/application-shell.js +82 -28
  65. package/lib/browser/shell/application-shell.js.map +1 -1
  66. package/lib/browser/shell/index.d.ts +1 -0
  67. package/lib/browser/shell/index.d.ts.map +1 -1
  68. package/lib/browser/shell/index.js +1 -0
  69. package/lib/browser/shell/index.js.map +1 -1
  70. package/lib/browser/shell/sidebar-bottom-menu-widget.d.ts.map +1 -1
  71. package/lib/browser/shell/sidebar-bottom-menu-widget.js +2 -1
  72. package/lib/browser/shell/sidebar-bottom-menu-widget.js.map +1 -1
  73. package/lib/browser/shell/sidebar-menu-widget.d.ts +4 -1
  74. package/lib/browser/shell/sidebar-menu-widget.d.ts.map +1 -1
  75. package/lib/browser/shell/sidebar-menu-widget.js +14 -1
  76. package/lib/browser/shell/sidebar-menu-widget.js.map +1 -1
  77. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts +66 -8
  78. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -1
  79. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +161 -8
  80. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
  81. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +18 -32
  82. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -1
  83. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +52 -88
  84. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
  85. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts +17 -21
  86. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts.map +1 -1
  87. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js +9 -9
  88. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js.map +1 -1
  89. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +7 -39
  90. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  91. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +30 -238
  92. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  93. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +13 -13
  94. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
  95. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts +56 -0
  96. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts.map +1 -0
  97. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js +208 -0
  98. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js.map +1 -0
  99. package/lib/browser/shell/tab-bars.d.ts.map +1 -1
  100. package/lib/browser/shell/tab-bars.js +2 -1
  101. package/lib/browser/shell/tab-bars.js.map +1 -1
  102. package/lib/browser/shell/theia-dock-panel.d.ts +4 -10
  103. package/lib/browser/shell/theia-dock-panel.d.ts.map +1 -1
  104. package/lib/browser/shell/theia-dock-panel.js +7 -84
  105. package/lib/browser/shell/theia-dock-panel.js.map +1 -1
  106. package/lib/browser/shell/theia-split-panel.d.ts +6 -0
  107. package/lib/browser/shell/theia-split-panel.d.ts.map +1 -0
  108. package/lib/browser/shell/theia-split-panel.js +56 -0
  109. package/lib/browser/shell/theia-split-panel.js.map +1 -0
  110. package/lib/browser/tree/tree-widget.d.ts +1 -0
  111. package/lib/browser/tree/tree-widget.d.ts.map +1 -1
  112. package/lib/browser/tree/tree-widget.js +6 -0
  113. package/lib/browser/tree/tree-widget.js.map +1 -1
  114. package/lib/browser/view-container.d.ts +6 -3
  115. package/lib/browser/view-container.d.ts.map +1 -1
  116. package/lib/browser/view-container.js +36 -26
  117. package/lib/browser/view-container.js.map +1 -1
  118. package/lib/browser/window/default-secondary-window-service.d.ts +1 -0
  119. package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
  120. package/lib/browser/window/default-secondary-window-service.js +3 -0
  121. package/lib/browser/window/default-secondary-window-service.js.map +1 -1
  122. package/lib/common/listener.d.ts +21 -0
  123. package/lib/common/listener.d.ts.map +1 -0
  124. package/lib/common/listener.js +81 -0
  125. package/lib/common/listener.js.map +1 -0
  126. package/lib/common/listener.spec.d.ts +2 -0
  127. package/lib/common/listener.spec.d.ts.map +1 -0
  128. package/lib/common/listener.spec.js +255 -0
  129. package/lib/common/listener.spec.js.map +1 -0
  130. package/lib/common/menu/index.d.ts +2 -3
  131. package/lib/common/menu/index.d.ts.map +1 -1
  132. package/lib/common/menu/index.js +2 -3
  133. package/lib/common/menu/index.js.map +1 -1
  134. package/lib/common/menu/menu-model-registry.d.ts +37 -50
  135. package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
  136. package/lib/common/menu/menu-model-registry.js +176 -225
  137. package/lib/common/menu/menu-model-registry.js.map +1 -1
  138. package/lib/common/menu/menu-types.d.ts +58 -96
  139. package/lib/common/menu/menu-types.d.ts.map +1 -1
  140. package/lib/common/menu/menu-types.js +43 -39
  141. package/lib/common/menu/menu-types.js.map +1 -1
  142. package/lib/common/messaging/proxy-factory.d.ts.map +1 -1
  143. package/lib/common/messaging/proxy-factory.js +4 -0
  144. package/lib/common/messaging/proxy-factory.js.map +1 -1
  145. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts +15 -5
  146. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
  147. package/lib/electron-browser/menu/electron-context-menu-renderer.js +21 -14
  148. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  149. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +4 -16
  150. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  151. package/lib/electron-browser/menu/electron-main-menu-factory.js +84 -104
  152. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  153. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  154. package/lib/electron-browser/menu/electron-menu-contribution.js +1 -4
  155. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  156. package/lib/electron-browser/menu/electron-menu-module.d.ts.map +1 -1
  157. package/lib/electron-browser/menu/electron-menu-module.js +5 -0
  158. package/lib/electron-browser/menu/electron-menu-module.js.map +1 -1
  159. package/lib/electron-browser/window/electron-secondary-window-service.d.ts +1 -0
  160. package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
  161. package/lib/electron-browser/window/electron-secondary-window-service.js +20 -0
  162. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  163. package/lib/electron-browser/window/electron-window-service.d.ts +3 -0
  164. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  165. package/lib/electron-browser/window/electron-window-service.js +10 -1
  166. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  167. package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
  168. package/lib/electron-main/theia-electron-window.js +2 -0
  169. package/lib/electron-main/theia-electron-window.js.map +1 -1
  170. package/package.json +7 -8
  171. package/src/browser/common-frontend-contribution.ts +14 -14
  172. package/src/browser/context-menu-renderer.ts +33 -5
  173. package/src/browser/frontend-application-module.ts +1 -7
  174. package/src/browser/hover-service.ts +7 -0
  175. package/src/browser/menu/action-menu-node.ts +128 -0
  176. package/src/browser/menu/browser-context-menu-renderer.ts +18 -11
  177. package/src/browser/menu/browser-menu-module.ts +4 -0
  178. package/src/browser/menu/browser-menu-node-factory.ts +48 -0
  179. package/src/browser/menu/browser-menu-plugin.ts +80 -168
  180. package/src/browser/menu/composite-menu-node.ts +140 -0
  181. package/src/{common → browser}/menu/menu.spec.ts +47 -15
  182. package/src/browser/open-with-service.ts +1 -1
  183. package/src/browser/saveable-service.ts +6 -1
  184. package/src/browser/shell/application-shell.ts +91 -29
  185. package/src/browser/shell/index.ts +1 -0
  186. package/src/browser/shell/sidebar-bottom-menu-widget.tsx +2 -1
  187. package/src/browser/shell/sidebar-menu-widget.tsx +12 -2
  188. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +239 -0
  189. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +59 -102
  190. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +14 -23
  191. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.ts +14 -14
  192. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +34 -261
  193. package/src/browser/shell/tab-bar-toolbar/tab-toolbar-item.tsx +251 -0
  194. package/src/browser/shell/tab-bars.ts +2 -1
  195. package/src/browser/shell/theia-dock-panel.ts +10 -91
  196. package/src/browser/shell/theia-split-panel.ts +56 -0
  197. package/src/browser/style/hover-service.css +6 -1
  198. package/src/browser/style/index.css +3 -11
  199. package/src/browser/style/view-container.css +17 -31
  200. package/src/browser/tree/tree-widget.tsx +7 -0
  201. package/src/browser/view-container.ts +51 -30
  202. package/src/browser/window/default-secondary-window-service.ts +4 -0
  203. package/src/common/listener.spec.ts +315 -0
  204. package/src/common/listener.ts +88 -0
  205. package/src/common/menu/index.ts +2 -3
  206. package/src/common/menu/menu-model-registry.ts +187 -230
  207. package/src/common/menu/menu-types.ts +82 -128
  208. package/src/common/messaging/proxy-factory.ts +4 -1
  209. package/src/electron-browser/menu/electron-context-menu-renderer.ts +29 -13
  210. package/src/electron-browser/menu/electron-main-menu-factory.ts +92 -116
  211. package/src/electron-browser/menu/electron-menu-contribution.ts +1 -4
  212. package/src/electron-browser/menu/electron-menu-module.ts +6 -1
  213. package/src/electron-browser/window/electron-secondary-window-service.ts +22 -0
  214. package/src/electron-browser/window/electron-window-service.ts +11 -1
  215. package/src/electron-main/theia-electron-window.ts +2 -0
  216. package/lib/common/menu/action-menu-node.d.ts +0 -20
  217. package/lib/common/menu/action-menu-node.d.ts.map +0 -1
  218. package/lib/common/menu/action-menu-node.js +0 -57
  219. package/lib/common/menu/action-menu-node.js.map +0 -1
  220. package/lib/common/menu/composite-menu-node.d.ts +0 -47
  221. package/lib/common/menu/composite-menu-node.d.ts.map +0 -1
  222. package/lib/common/menu/composite-menu-node.js +0 -96
  223. package/lib/common/menu/composite-menu-node.js.map +0 -1
  224. package/lib/common/menu/composite-menu-node.spec.d.ts +0 -2
  225. package/lib/common/menu/composite-menu-node.spec.d.ts.map +0 -1
  226. package/lib/common/menu/composite-menu-node.spec.js +0 -68
  227. package/lib/common/menu/composite-menu-node.spec.js.map +0 -1
  228. package/lib/common/menu/menu-adapter.d.ts +0 -36
  229. package/lib/common/menu/menu-adapter.d.ts.map +0 -1
  230. package/lib/common/menu/menu-adapter.js +0 -93
  231. package/lib/common/menu/menu-adapter.js.map +0 -1
  232. package/lib/common/menu/menu.spec.d.ts.map +0 -1
  233. package/lib/common/menu/menu.spec.js.map +0 -1
  234. package/lib/common/test/mock-menu.d.ts +0 -8
  235. package/lib/common/test/mock-menu.d.ts.map +0 -1
  236. package/lib/common/test/mock-menu.js +0 -35
  237. package/lib/common/test/mock-menu.js.map +0 -1
  238. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts +0 -31
  239. package/src/common/menu/action-menu-node.ts +0 -65
  240. package/src/common/menu/composite-menu-node.spec.ts +0 -67
  241. package/src/common/menu/composite-menu-node.ts +0 -116
  242. package/src/common/menu/menu-adapter.ts +0 -103
  243. package/src/common/test/mock-menu.ts +0 -35
  244. /package/lib/{common → browser}/menu/menu.spec.d.ts +0 -0
@@ -0,0 +1,315 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 ST Microelectronics 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-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ /* eslint-disable @typescript-eslint/no-explicit-any */
18
+
19
+ import { expect } from 'chai';
20
+ import { ListenerList } from './listener';
21
+
22
+ describe('ListenerList<T, U>', () => {
23
+ let listenerList: ListenerList<any, any>;
24
+
25
+ beforeEach(() => {
26
+ listenerList = new ListenerList<any, any>();
27
+ });
28
+
29
+ describe('registration and disposal', () => {
30
+ it('should register a single listener and allow it to be invoked', () => {
31
+ let listenerCalled = false;
32
+ const testEvent = { data: 'test' };
33
+ let receivedEvent: any;
34
+ const listener = (e: any) => {
35
+ listenerCalled = true;
36
+ receivedEvent = e;
37
+ return 'result';
38
+ };
39
+
40
+ listenerList.registration(listener);
41
+
42
+ let callbackCalled = false;
43
+ let callbackValue: any;
44
+ listenerList.invoke(testEvent, value => {
45
+ callbackCalled = true;
46
+ callbackValue = value;
47
+ });
48
+
49
+ expect(listenerCalled).to.be.true;
50
+ expect(receivedEvent).to.equal(testEvent);
51
+ expect(callbackCalled).to.be.true;
52
+ expect(callbackValue).to.equal('result');
53
+ });
54
+
55
+ it('should return a Disposable when a listener is registered', () => {
56
+ const listener = () => { };
57
+ const disposable = listenerList.registration(listener);
58
+ expect(disposable).to.exist;
59
+ expect(typeof disposable.dispose).to.equal('function');
60
+ });
61
+
62
+ it('should remove the listener when the Disposable is disposed', () => {
63
+ let listenerCallCount = 0;
64
+ const listener = () => {
65
+ listenerCallCount++;
66
+ };
67
+
68
+ const disposable = listenerList.registration(listener);
69
+ disposable.dispose();
70
+
71
+ listenerList.invoke({}, () => { });
72
+
73
+ expect(listenerCallCount).to.equal(0);
74
+ });
75
+
76
+ it('should register multiple listeners and invoke all of them', () => {
77
+ let listener1Called = false;
78
+ let listener2Called = false;
79
+ const results: any[] = [];
80
+
81
+ const listener1 = (e: any) => { listener1Called = true; return e + 1; };
82
+ const listener2 = (e: any) => { listener2Called = true; return e + 2; };
83
+
84
+ listenerList.registration(listener1);
85
+ listenerList.registration(listener2);
86
+
87
+ listenerList.invoke(10, value => {
88
+ results.push(value);
89
+ });
90
+
91
+ expect(listener1Called).to.be.true;
92
+ expect(listener2Called).to.be.true;
93
+ expect(results).to.deep.equal([11, 12]);
94
+ });
95
+
96
+ it('should dispose of a specific listener among multiple, leaving others active', () => {
97
+ let listener1CallCount = 0;
98
+ let listener2CallCount = 0;
99
+ const results: any[] = [];
100
+
101
+ const listener1 = (e: any) => { listener1CallCount++; return 'L1'; };
102
+ const listener2 = (e: any) => { listener2CallCount++; return 'L2'; };
103
+
104
+ const disposable1 = listenerList.registration(listener1);
105
+ listenerList.registration(listener2);
106
+
107
+ disposable1.dispose();
108
+
109
+ listenerList.invoke({}, value => {
110
+ results.push(value);
111
+ });
112
+
113
+ expect(listener1CallCount).to.equal(0);
114
+ expect(listener2CallCount).to.equal(1);
115
+ expect(results).to.deep.equal(['L2']);
116
+ });
117
+
118
+ it('should handle disposing a listener that was already disposed (no error)', () => {
119
+ const listener = () => { };
120
+ const disposable = listenerList.registration(listener);
121
+
122
+ disposable.dispose(); // First dispose
123
+ expect(() => disposable.dispose()).to.not.throw(); // Second dispose
124
+
125
+ // Ensure no listeners are invoked
126
+ let invokeCallbackCalled = false;
127
+ listenerList.invoke({}, () => { invokeCallbackCalled = true; });
128
+ expect(invokeCallbackCalled).to.be.false;
129
+ });
130
+
131
+ it('should correctly transition internal listeners from single object to array', () => {
132
+ const results: string[] = [];
133
+ const callback = (value: string) => results.push(value);
134
+
135
+ const listener1 = () => 'one';
136
+ listenerList.registration(listener1);
137
+ listenerList.invoke({}, callback);
138
+ expect(results).to.deep.equal(['one']);
139
+ results.length = 0; // Clear results
140
+
141
+ const listener2 = () => 'two';
142
+ listenerList.registration(listener2); // This should transition internal listeners to an array
143
+ listenerList.invoke({}, callback);
144
+ expect(results).to.deep.equal(['one', 'two']);
145
+ });
146
+
147
+ it('should correctly transition internal listeners from array to single object or undefined upon disposal', () => {
148
+ const results: string[] = [];
149
+ const callback = (value: string) => results.push(value);
150
+
151
+ const listener1 = () => 'one';
152
+ const listener2 = () => 'two';
153
+ const listener3 = () => 'three';
154
+
155
+ const d1 = listenerList.registration(listener1);
156
+ const d2 = listenerList.registration(listener2);
157
+ const d3 = listenerList.registration(listener3); // listeners: [l1, l2, l3]
158
+
159
+ d2.dispose(); // listeners: [l1, l3]
160
+ listenerList.invoke({}, callback);
161
+ expect(results).to.deep.equal(['one', 'three']);
162
+ results.length = 0;
163
+
164
+ d1.dispose(); // listeners: [l3] (should become single object)
165
+ listenerList.invoke({}, callback);
166
+ expect(results).to.deep.equal(['three']);
167
+ results.length = 0;
168
+
169
+ // Verify it's a single object by checking the internal structure if possible,
170
+ // or by behavior (e.g. adding another makes it an array of two)
171
+ // For this test, we'll infer from behavior that if it works, it's correct.
172
+ // Add another listener, then remove the current single one (d3)
173
+ const listener4 = () => 'four';
174
+ const d4 = listenerList.registration(listener4); // Now should be [l3, l4]
175
+ listenerList.invoke({}, callback);
176
+ expect(results).to.include.members(['three', 'four']);
177
+ expect(results.length).to.equal(2);
178
+ results.length = 0;
179
+
180
+ d3.dispose(); // Now should be [l4] (single object)
181
+ listenerList.invoke({}, callback);
182
+ expect(results).to.deep.equal(['four']);
183
+ results.length = 0;
184
+
185
+ d4.dispose();
186
+ listenerList.invoke({}, callback);
187
+ expect(results.length).to.equal(0);
188
+ });
189
+
190
+ it('should correctly transition listeners to empty when last listener is removed', () => {
191
+ const results: string[] = [];
192
+ const callback = (value: string) => results.push(value);
193
+
194
+ const listener1 = () => 'one';
195
+ const d1 = listenerList.registration(listener1);
196
+ listenerList.invoke({}, callback);
197
+ expect(results).to.deep.equal(['one']);
198
+ results.length = 0;
199
+
200
+ d1.dispose();
201
+ listenerList.invoke({}, callback);
202
+ expect(results.length).to.equal(0);
203
+ });
204
+ });
205
+
206
+ describe('invoke', () => {
207
+ it('should not invoke callback if no listeners are registered', () => {
208
+ let callbackCalled = false;
209
+ listenerList.invoke({}, () => {
210
+ callbackCalled = true;
211
+ });
212
+ expect(callbackCalled).to.be.false;
213
+ });
214
+
215
+ it('should pass the event object correctly to a single listener', () => {
216
+ const testEvent = { id: 1, value: 'data' };
217
+ let receivedEvent: any = undefined;
218
+ const listener = (e: any) => {
219
+ receivedEvent = e;
220
+ };
221
+ listenerList.registration(listener);
222
+ listenerList.invoke(testEvent, () => { });
223
+ expect(receivedEvent).to.deep.equal(testEvent);
224
+ });
225
+
226
+ it('should pass the event object correctly to multiple listeners', () => {
227
+ const testEvent = { id: 2, value: 'more data' };
228
+ let eventForL1: any = undefined;
229
+ let eventForL2: any = undefined;
230
+
231
+ const listener1 = (e: any) => { eventForL1 = e; };
232
+ const listener2 = (e: any) => { eventForL2 = e; };
233
+
234
+ listenerList.registration(listener1);
235
+ listenerList.registration(listener2);
236
+ listenerList.invoke(testEvent, () => { });
237
+
238
+ expect(eventForL1).to.deep.equal(testEvent);
239
+ expect(eventForL2).to.deep.equal(testEvent);
240
+ });
241
+
242
+ it('should call the callback with the return value of a single listener', () => {
243
+ const expectedResult = 'unique_result';
244
+ const listener = () => expectedResult;
245
+ listenerList.registration(listener);
246
+
247
+ let actualResult: any;
248
+ listenerList.invoke({}, value => {
249
+ actualResult = value;
250
+ });
251
+ expect(actualResult).to.equal(expectedResult);
252
+ });
253
+
254
+ it('should call the callback for each return value of multiple listeners', () => {
255
+ const resultL1 = 'res1';
256
+ const resultL2 = 'res2';
257
+ const collectedResults: any[] = [];
258
+
259
+ const listener1 = () => resultL1;
260
+ const listener2 = () => resultL2;
261
+
262
+ listenerList.registration(listener1);
263
+ listenerList.registration(listener2);
264
+
265
+ listenerList.invoke({}, value => {
266
+ collectedResults.push(value);
267
+ });
268
+
269
+ expect(collectedResults).to.have.lengthOf(2);
270
+ expect(collectedResults).to.include.members([resultL1, resultL2]);
271
+ });
272
+
273
+ it('should not invoke a listener after it has been disposed (re-check with invoke)', () => {
274
+ let callCount = 0;
275
+ const listener = () => { callCount++; };
276
+ const disposable = listenerList.registration(listener);
277
+
278
+ listenerList.invoke({}, () => { }); // Should call once
279
+ expect(callCount).to.equal(1);
280
+
281
+ disposable.dispose();
282
+ listenerList.invoke({}, () => { }); // Should not call again
283
+ expect(callCount).to.equal(1); // Still 1
284
+ });
285
+
286
+ it('should handle invocation when listeners are added and removed dynamically', () => {
287
+ const event1 = 'event1';
288
+ const event2 = 'event2';
289
+ const results: string[] = [];
290
+ const invokeCallback = (val: string) => results.push(val);
291
+
292
+ const listenerA = (e: string) => `A(${e})`;
293
+ const listenerB = (e: string) => `B(${e})`;
294
+
295
+ const dA = listenerList.registration(listenerA);
296
+ listenerList.invoke(event1, invokeCallback);
297
+ expect(results).to.deep.equal([`A(${event1})`]);
298
+ results.length = 0;
299
+
300
+ const dB = listenerList.registration(listenerB);
301
+ listenerList.invoke(event1, invokeCallback);
302
+ expect(results).to.deep.equal([`A(${event1})`, `B(${event1})`]); // Order is preserved
303
+ results.length = 0;
304
+
305
+ dA.dispose();
306
+ listenerList.invoke(event2, invokeCallback);
307
+ expect(results).to.deep.equal([`B(${event2})`]);
308
+ results.length = 0;
309
+
310
+ dB.dispose();
311
+ listenerList.invoke(event2, invokeCallback);
312
+ expect(results).to.deep.equal([]);
313
+ });
314
+ });
315
+ });
@@ -0,0 +1,88 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 ST Microelectronics 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-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Disposable } from './disposable';
18
+
19
+ /**
20
+ * Ths code in this file implements a list of listeners that can be invoked sequentially with a callback to handle
21
+ * the results of the listener invocation.
22
+ */
23
+
24
+ type Listener<T, U> = (e: T) => U;
25
+ interface ListenerRegistration<T, U> {
26
+ id: number,
27
+ listener: Listener<T, U>;
28
+ }
29
+
30
+ export namespace Listener {
31
+ export type Registration<T, U = void> = (listener: (e: T) => U) => Disposable;
32
+ export const None: Registration<void, void> = () => Disposable.NULL;
33
+
34
+ /**
35
+ * Convenience function to await all listener invocations
36
+ * @param value The value to invoke the listeners with
37
+ * @param list the listener list to invoke
38
+ * @returns the return values from the listener invocation
39
+ */
40
+ export async function await<T, U>(value: T, list: ListenerList<T, Promise<U>>): Promise<U[]> {
41
+ const promises: Promise<U>[] = [];
42
+ list.invoke(value, promise => {
43
+ promises.push(promise);
44
+ });
45
+ return await Promise.all(promises);
46
+ }
47
+ }
48
+
49
+ export class ListenerList<T, U> {
50
+ private listeners: ListenerRegistration<T, U> | (ListenerRegistration<T, U>)[] | undefined;
51
+ private registeredCount = 1; // start at 1 to prevent falsy madness
52
+
53
+ registration: Listener.Registration<T, U> = this.register.bind(this);
54
+
55
+ private register(listener: Listener<T, U>): Disposable {
56
+ const reg: ListenerRegistration<T, U> = { id: this.registeredCount++, listener };
57
+ if (!this.listeners) {
58
+ this.listeners = reg;
59
+ } else if (Array.isArray(this.listeners)) {
60
+ this.listeners.push(reg as ListenerRegistration<T, U>);
61
+ } else {
62
+ this.listeners = [this.listeners, reg];
63
+ }
64
+ return Disposable.create(() => {
65
+ this.remove(reg.id);
66
+ });
67
+ }
68
+ private remove(id: number): void {
69
+ if (Array.isArray(this.listeners)) {
70
+ const index = this.listeners.findIndex(v => v.id === id);
71
+ if (index >= 0) {
72
+ this.listeners.splice(index, 1);
73
+ }
74
+ } else if (this.listeners && this.listeners.id === id) {
75
+ this.listeners = undefined;
76
+ }
77
+ }
78
+
79
+ invoke(e: T, callback: (value: U) => void): void {
80
+ if (Array.isArray(this.listeners)) {
81
+ for (const l of this.listeners) {
82
+ callback(l.listener(e));
83
+ }
84
+ } else if (this.listeners) {
85
+ callback(this.listeners.listener(e));
86
+ }
87
+ }
88
+ }
@@ -14,8 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- export * from './action-menu-node';
18
- export * from './composite-menu-node';
19
- export * from './menu-adapter';
17
+ export * from '../../browser/menu/action-menu-node';
18
+ export * from '../../browser/menu/composite-menu-node';
20
19
  export * from './menu-model-registry';
21
20
  export * from './menu-types';