@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
@@ -15,13 +15,12 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { inject, injectable, named } from 'inversify';
18
- import { Command, CommandRegistry } from '../command';
18
+ import { CommandMenu, CompoundMenuNode, Group, MAIN_MENU_BAR, MenuAction, MenuNode, MenuPath, MutableCompoundMenuNode, Submenu } from './menu-types';
19
+ import { Event } from 'vscode-languageserver-protocol';
19
20
  import { ContributionProvider } from '../contribution-provider';
21
+ import { Command, CommandRegistry } from '../command';
22
+ import { Emitter } from '../event';
20
23
  import { Disposable } from '../disposable';
21
- import { Emitter, Event } from '../event';
22
- import { ActionMenuNode } from './action-menu-node';
23
- import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node';
24
- import { CompoundMenuNode, MenuAction, MenuNode, MenuNodeMetadata, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types';
25
24
 
26
25
  export const MenuContribution = Symbol('MenuContribution');
27
26
 
@@ -81,6 +80,15 @@ export namespace StructuralMenuChange {
81
80
  return evt.kind !== ChangeKind.CHANGED;
82
81
  }
83
82
  }
83
+ export const MenuNodeFactory = Symbol('MenuNodeFactory');
84
+
85
+ export interface MenuNodeFactory {
86
+ createGroup(id: string, orderString?: string, when?: string): Group & MutableCompoundMenuNode;
87
+ createCommandMenu(item: MenuAction): CommandMenu;
88
+ createSubmenu(id: string, label: string, contextKeyOverlays: Record<string, string> | undefined,
89
+ orderString?: string, icon?: string, when?: string): Submenu & MutableCompoundMenuNode
90
+ createSubmenuLink(delegate: Submenu, sortString?: string, when?: string): MenuNode;
91
+ }
84
92
 
85
93
  /**
86
94
  * The MenuModelRegistry allows to register and unregister menus, submenus and actions
@@ -89,23 +97,27 @@ export namespace StructuralMenuChange {
89
97
  */
90
98
  @injectable()
91
99
  export class MenuModelRegistry {
92
- protected readonly root = new CompositeMenuNode('');
93
- protected readonly independentSubmenus = new Map<string, MutableCompoundMenuNode>();
100
+ protected root: Group & MutableCompoundMenuNode;
94
101
 
95
102
  protected readonly onDidChangeEmitter = new Emitter<MenuChangedEvent>();
96
103
 
104
+ constructor(
105
+ @inject(ContributionProvider) @named(MenuContribution)
106
+ protected readonly contributions: ContributionProvider<MenuContribution>,
107
+ @inject(CommandRegistry)
108
+ protected readonly commands: CommandRegistry,
109
+ @inject(MenuNodeFactory)
110
+ protected readonly menuNodeFactory: MenuNodeFactory) {
111
+ this.root = this.menuNodeFactory.createGroup('root', 'root');
112
+ this.root.addNode(this.menuNodeFactory.createGroup(MAIN_MENU_BAR[0]));
113
+ }
114
+
97
115
  get onDidChange(): Event<MenuChangedEvent> {
98
116
  return this.onDidChangeEmitter.event;
99
117
  }
100
118
 
101
119
  protected isReady = false;
102
120
 
103
- constructor(
104
- @inject(ContributionProvider) @named(MenuContribution)
105
- protected readonly contributions: ContributionProvider<MenuContribution>,
106
- @inject(CommandRegistry) protected readonly commands: CommandRegistry
107
- ) { }
108
-
109
121
  onStart(): void {
110
122
  for (const contrib of this.contributions.getContributions()) {
111
123
  contrib.registerMenus(this);
@@ -118,47 +130,48 @@ export class MenuModelRegistry {
118
130
  *
119
131
  * @returns a disposable which, when called, will remove the menu action again.
120
132
  */
121
- registerMenuAction(menuPath: MenuPath, item: MenuAction): Disposable {
122
- const menuNode = new ActionMenuNode(item, this.commands);
123
- return this.registerMenuNode(menuPath, menuNode);
133
+ registerCommandMenu(menuPath: MenuPath, item: CommandMenu): Disposable {
134
+ const parent = this.root.getOrCreate(menuPath, 0, menuPath.length);
135
+ const existing = parent.children.find(node => node.id === menuPath[menuPath.length - 1]);
136
+ if (existing) {
137
+ throw new Error(`A menu node with path ${JSON.stringify(menuPath)} already exists`);
138
+ } else {
139
+ parent.addNode(item);
140
+ return Disposable.create(() => {
141
+ parent.removeNode(item);
142
+ this.fireChangeEvent({
143
+ kind: ChangeKind.REMOVED,
144
+ path: menuPath.slice(0, menuPath.length - 1),
145
+ affectedChildId: item.id
146
+ });
147
+ });
148
+ }
149
+
124
150
  }
125
151
 
126
152
  /**
127
- * Adds the given menu node to the menu denoted by the given path.
153
+ * Adds the given menu action to the menu denoted by the given path.
128
154
  *
129
- * @returns a disposable which, when called, will remove the menu node again.
155
+ * @returns a disposable which, when called, will remove the menu action again.
130
156
  */
131
- registerMenuNode(menuPath: MenuPath | string, menuNode: MenuNode, group?: string): Disposable {
132
- const parent = this.getMenuNode(menuPath, group);
133
- const disposable = parent.addNode(menuNode);
134
- const parentPath = this.getParentPath(menuPath, group);
135
- this.fireChangeEvent({
136
- kind: ChangeKind.ADDED,
137
- path: parentPath,
138
- affectedChildId: menuNode.id
139
- });
140
- return this.changeEventOnDispose(parentPath, menuNode.id, disposable);
141
- }
142
-
143
- protected getParentPath(menuPath: MenuPath | string, group?: string): string[] {
144
- if (typeof menuPath === 'string') {
145
- return group ? [menuPath, group] : [menuPath];
157
+ registerMenuAction(menuPath: MenuPath, item: MenuAction): Disposable {
158
+ const parent = this.root.getOrCreate(menuPath, 0, menuPath.length);
159
+ const existing = parent.children.find(node => node.id === item.commandId);
160
+ if (existing) {
161
+ throw new Error(`A menu node with id ${item.commandId} in path ${JSON.stringify(menuPath)} already exists`);
146
162
  } else {
147
- return group ? menuPath.concat(group) : menuPath;
163
+ const node = this.menuNodeFactory.createCommandMenu(item);
164
+ parent.addNode(node);
165
+ return Disposable.create(() => {
166
+ parent.removeNode(node);
167
+ this.fireChangeEvent({
168
+ kind: ChangeKind.REMOVED,
169
+ path: menuPath.slice(0, menuPath.length - 1),
170
+ affectedChildId: node.id
171
+ });
172
+ });
148
173
  }
149
- }
150
174
 
151
- getMenuNode(menuPath: MenuPath | string, group?: string): MutableCompoundMenuNode {
152
- if (typeof menuPath === 'string') {
153
- const target = this.independentSubmenus.get(menuPath);
154
- if (!target) { throw new Error(`Could not find submenu with id ${menuPath}`); }
155
- if (group) {
156
- return this.findSubMenu(target, group);
157
- }
158
- return target;
159
- } else {
160
- return this.findGroup(group ? menuPath.concat(group) : menuPath);
161
- }
162
175
  }
163
176
 
164
177
  /**
@@ -176,72 +189,84 @@ export class MenuModelRegistry {
176
189
  * Note that if the menu already existed and was registered with a different label an error
177
190
  * will be thrown.
178
191
  */
179
- registerSubmenu(menuPath: MenuPath, label: string, options?: SubMenuOptions): Disposable {
180
- if (menuPath.length === 0) {
181
- throw new Error('The sub menu path cannot be empty.');
182
- }
183
- const index = menuPath.length - 1;
184
- const menuId = menuPath[index];
185
- const groupPath = index === 0 ? [] : menuPath.slice(0, index);
186
- const parent = this.findGroup(groupPath, options);
187
- let groupNode = this.findSubMenu(parent, menuId, options);
188
- let disposable = Disposable.NULL;
189
- if (!groupNode) {
190
- groupNode = new CompositeMenuNode(menuId, label, options, parent);
191
- disposable = this.changeEventOnDispose(groupPath, menuId, parent.addNode(groupNode));
192
+ registerSubmenu(menuPath: MenuPath, label: string,
193
+ options: { sortString?: string, icon?: string, when?: string, contextKeyOverlay?: Record<string, string> } = {}): Disposable {
194
+ const { contextKeyOverlay, sortString, icon, when } = options;
195
+
196
+ const parent = this.root.getOrCreate(menuPath, 0, menuPath.length - 1);
197
+ const existing = parent.children.find(node => node.id === menuPath[menuPath.length - 1]);
198
+ if (Group.is(existing)) {
199
+ parent.removeNode(existing);
200
+ const newMenu = this.menuNodeFactory.createSubmenu(menuPath[menuPath.length - 1], label, contextKeyOverlay, sortString, icon, when);
201
+ newMenu.addNode(...existing.children);
202
+ parent.addNode(newMenu);
192
203
  this.fireChangeEvent({
193
- kind: ChangeKind.ADDED,
194
- path: groupPath,
195
- affectedChildId: menuId
204
+ kind: ChangeKind.CHANGED,
205
+ path: menuPath
206
+ });
207
+ return Disposable.create(() => {
208
+ parent.removeNode(newMenu);
209
+ this.fireChangeEvent({
210
+ kind: ChangeKind.REMOVED,
211
+ path: menuPath.slice(0, menuPath.length - 1),
212
+ affectedChildId: newMenu.id
213
+ });
196
214
  });
197
215
  } else {
216
+ const newMenu = this.menuNodeFactory.createSubmenu(menuPath[menuPath.length - 1], label, contextKeyOverlay, sortString, icon, when);
217
+ parent.addNode(newMenu);
198
218
  this.fireChangeEvent({
199
- kind: ChangeKind.CHANGED,
200
- path: groupPath,
219
+ kind: ChangeKind.ADDED,
220
+ path: menuPath.slice(0, menuPath.length - 1),
221
+ affectedChildId: newMenu.id
222
+ });
223
+ return Disposable.create(() => {
224
+ parent.removeNode(newMenu);
225
+ this.fireChangeEvent({
226
+ kind: ChangeKind.REMOVED,
227
+ path: menuPath.slice(0, menuPath.length - 1),
228
+ affectedChildId: newMenu.id
229
+ });
201
230
  });
202
- groupNode.updateOptions({ ...options, label });
203
231
  }
204
- return disposable;
205
232
  }
206
233
 
207
- registerIndependentSubmenu(id: string, label: string, options?: SubMenuOptions): Disposable {
208
- if (this.independentSubmenus.has(id)) {
209
- console.debug(`Independent submenu with path ${id} registered, but given ID already exists.`);
234
+ linkCompoundMenuNode(params: { newParentPath: MenuPath, submenuPath: MenuPath, order?: string, when?: string }): Disposable {
235
+ const { newParentPath, submenuPath, order, when } = params;
236
+ // add a wrapper here
237
+ let i = 0;
238
+ while (i < newParentPath.length && i < submenuPath.length && newParentPath[i] === submenuPath[i]) {
239
+ i++;
210
240
  }
211
- this.independentSubmenus.set(id, new CompositeMenuNode(id, label, options));
212
- return this.changeEventOnDispose([], id, Disposable.create(() => this.independentSubmenus.delete(id)));
213
- }
214
-
215
- linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable {
216
- const child = this.getMenuNode(childId);
217
- const parent = this.getMenuNode(parentPath, group);
218
- const affectedPath = this.getParentPath(parentPath, group);
219
241
 
220
- const isRecursive = (node: MenuNodeMetadata, childNode: MenuNodeMetadata): boolean => {
221
- if (node.id === childNode.id) {
222
- return true;
223
- }
224
- if (node.parent) {
225
- return isRecursive(node.parent, childNode);
226
- }
227
- return false;
228
- };
229
-
230
- // check for menu contribution recursion
231
- if (isRecursive(parent, child)) {
232
- console.warn(`Recursive menu contribution detected: ${child.id} is already in hierarchy of ${parent.id}.`);
233
- return Disposable.NULL;
242
+ if (i === newParentPath.length || i === submenuPath.length) {
243
+ throw new Error(`trying to recursively link ${JSON.stringify(submenuPath)} into ${JSON.stringify(newParentPath)}`);
234
244
  }
235
245
 
236
- const wrapper = new CompositeMenuNodeWrapper(child, parent, options);
237
- const disposable = parent.addNode(wrapper);
238
- this.fireChangeEvent({
239
- kind: ChangeKind.LINKED,
240
- path: affectedPath,
241
- affectedChildId: child.id
242
-
243
- });
244
- return this.changeEventOnDispose(affectedPath, child.id, disposable);
246
+ const child = this.getMenu(submenuPath) as Submenu;
247
+ if (!child) {
248
+ throw new Error(`Not a menu node: ${JSON.stringify(submenuPath)}`);
249
+ }
250
+ const newParent = this.root.getOrCreate(newParentPath, 0, newParentPath.length);
251
+ if (MutableCompoundMenuNode.is(newParent)) {
252
+ const link = this.menuNodeFactory.createSubmenuLink(child, order, when);
253
+ newParent.addNode(link);
254
+ this.fireChangeEvent({
255
+ kind: ChangeKind.LINKED,
256
+ path: newParentPath,
257
+ affectedChildId: child.id
258
+ });
259
+ return Disposable.create(() => {
260
+ newParent.removeNode(link);
261
+ this.fireChangeEvent({
262
+ kind: ChangeKind.REMOVED,
263
+ path: newParentPath,
264
+ affectedChildId: child.id
265
+ });
266
+ });
267
+ } else {
268
+ throw new Error(`Not a compound menu node: ${JSON.stringify(newParentPath)}`);
269
+ }
245
270
  }
246
271
 
247
272
  /**
@@ -265,89 +290,58 @@ export class MenuModelRegistry {
265
290
  * @param menuPath if specified only nodes within the path will be unregistered.
266
291
  */
267
292
  unregisterMenuAction(id: string, menuPath?: MenuPath): void;
268
- unregisterMenuAction(itemOrCommandOrId: MenuAction | Command | string, menuPath?: MenuPath): void {
293
+ unregisterMenuAction(itemOrCommandOrId: MenuAction | Command | string, menuPath: MenuPath = []): void {
269
294
  const id = MenuAction.is(itemOrCommandOrId) ? itemOrCommandOrId.commandId
270
295
  : Command.is(itemOrCommandOrId) ? itemOrCommandOrId.id
271
296
  : itemOrCommandOrId;
272
297
 
273
- if (menuPath) {
274
- const parent = this.findGroup(menuPath);
275
- parent.removeNode(id);
276
- this.fireChangeEvent({
277
- kind: ChangeKind.REMOVED,
278
- path: menuPath,
279
- affectedChildId: id
280
- });
281
- } else {
282
- this.unregisterMenuNode(id);
298
+ const parent = this.findInNode(this.root, menuPath, 0);
299
+ if (parent) {
300
+ this.removeActionInSubtree(parent, id);
283
301
  }
284
302
  }
285
303
 
286
- /**
287
- * Recurse all menus, removing any menus matching the `id`.
288
- *
289
- * @param id technical identifier of the `MenuNode`.
290
- */
291
- unregisterMenuNode(id: string): void {
292
- const parentPath: string[] = [];
293
- const recurse = (root: MutableCompoundMenuNode) => {
294
- root.children.forEach(node => {
295
- if (CompoundMenuNode.isMutable(node)) {
296
- if (node.removeNode(id)) {
297
- this.fireChangeEvent({
298
- kind: ChangeKind.REMOVED,
299
- path: parentPath,
300
- affectedChildId: id
301
- });
302
- }
303
- parentPath.push(node.id);
304
- recurse(node);
305
- parentPath.pop();
306
- }
307
- });
308
- };
309
- recurse(this.root);
304
+ protected removeActionInSubtree(parent: MenuNode, id: string): void {
305
+ if (MutableCompoundMenuNode.is(parent) && CompoundMenuNode.is(parent)) {
306
+ const action = parent.children.find(child => child.id === id);
307
+ if (action) {
308
+ parent.removeNode(action);
309
+ }
310
+ parent.children.forEach(child => this.removeActionInSubtree(child, id));
311
+ }
310
312
  }
311
313
 
312
- /**
313
- * Finds a submenu as a descendant of the `root` node.
314
- * See {@link MenuModelRegistry.findSubMenu findSubMenu}.
315
- */
316
- protected findGroup(menuPath: MenuPath, options?: SubMenuOptions): MutableCompoundMenuNode {
317
- let currentMenu: MutableCompoundMenuNode = this.root;
318
- for (const segment of menuPath) {
319
- currentMenu = this.findSubMenu(currentMenu, segment, options);
314
+ protected findInNode(root: CompoundMenuNode, menuPath: MenuPath, pathIndex: number): MenuNode | undefined {
315
+ if (pathIndex === menuPath.length) {
316
+ return root;
320
317
  }
321
- return currentMenu;
318
+ const child = root.children.find(c => c.id === menuPath[pathIndex]);
319
+ if (CompoundMenuNode.is(child)) {
320
+ return this.findInNode(child, menuPath, pathIndex + 1);
321
+ }
322
+ return undefined;
322
323
  }
323
324
 
324
- /**
325
- * Finds or creates a submenu as an immediate child of `current`.
326
- * @throws if a node with the given `menuId` exists but is not a {@link MutableCompoundMenuNode}.
327
- */
328
- protected findSubMenu(current: MutableCompoundMenuNode, menuId: string, options?: SubMenuOptions): MutableCompoundMenuNode {
329
- const sub = current.children.find(e => e.id === menuId);
330
- if (CompoundMenuNode.isMutable(sub)) {
331
- return sub;
332
- }
333
- if (sub) {
334
- throw new Error(`'${menuId}' is not a menu group.`);
325
+ getMenuNode(menuPath: string[]): MenuNode | undefined {
326
+ return this.findInNode(this.root, menuPath, 0);
327
+ }
328
+
329
+ getMenu(menuPath: MenuPath): CompoundMenuNode {
330
+ const node = this.getMenuNode(menuPath);
331
+ if (!CompoundMenuNode.is(node)) {
332
+ throw new Error(`not a compound menu node: ${JSON.stringify(menuPath)}`);
335
333
  }
336
- const newSub = new CompositeMenuNode(menuId, undefined, options, current);
337
- current.addNode(newSub);
338
- return newSub;
334
+ return node;
339
335
  }
340
336
 
341
- /**
342
- * Returns the menu at the given path.
343
- *
344
- * @param menuPath the path specifying the menu to return. If not given the empty path will be used.
345
- *
346
- * @returns the root menu when `menuPath` is empty. If `menuPath` is not empty the specified menu is
347
- * returned if it exists, otherwise an error is thrown.
348
- */
349
- getMenu(menuPath: MenuPath = []): MutableCompoundMenuNode {
350
- return this.findGroup(menuPath);
337
+ static removeSingleRootNodes(fullMenuModel: CompoundMenuNode): CompoundMenuNode {
338
+ let current = fullMenuModel;
339
+ let previous = undefined;
340
+ while (current !== previous) {
341
+ previous = current;
342
+ current = this.removeSingleRootNode(current);
343
+ }
344
+ return current;
351
345
  }
352
346
 
353
347
  /**
@@ -358,82 +352,45 @@ export class MenuModelRegistry {
358
352
  * @returns if the menu will show a single submenu this returns a menu that will show the child elements of the submenu,
359
353
  * otherwise the given `fullMenuModel` is return
360
354
  */
361
- removeSingleRootNode(fullMenuModel: MutableCompoundMenuNode, menuPath: MenuPath): CompoundMenuNode {
362
- // check whether all children are compound menus and that there is only one child that has further children
363
- if (!this.allChildrenCompound(fullMenuModel.children)) {
364
- return fullMenuModel;
365
- }
366
- let nonEmptyNode = undefined;
355
+ static removeSingleRootNode(fullMenuModel: CompoundMenuNode): CompoundMenuNode {
356
+
357
+ let singleChild = undefined;
358
+
367
359
  for (const child of fullMenuModel.children) {
368
- if (!this.isEmpty(child.children || [])) {
369
- if (nonEmptyNode === undefined) {
370
- nonEmptyNode = child;
371
- } else {
372
- return fullMenuModel;
360
+ if (CompoundMenuNode.is(child)) {
361
+ if (!MenuModelRegistry.isEmpty(child)) {
362
+ if (singleChild) {
363
+ return fullMenuModel;
364
+ } else {
365
+ singleChild = child;
366
+ }
373
367
  }
368
+ } else {
369
+ return fullMenuModel;
374
370
  }
375
371
  }
376
-
377
- if (CompoundMenuNode.is(nonEmptyNode) && nonEmptyNode.children.length === 1 && CompoundMenuNode.is(nonEmptyNode.children[0])) {
378
- nonEmptyNode = nonEmptyNode.children[0];
379
- }
380
-
381
- return CompoundMenuNode.is(nonEmptyNode) ? nonEmptyNode : fullMenuModel;
372
+ return singleChild || fullMenuModel;
382
373
  }
383
374
 
384
- protected allChildrenCompound(children: ReadonlyArray<MenuNode>): boolean {
385
- return children.every(CompoundMenuNode.is);
386
- }
387
-
388
- protected isEmpty(children: ReadonlyArray<MenuNode>): boolean {
389
- if (children.length === 0) {
390
- return true;
391
- }
392
- if (!this.allChildrenCompound(children)) {
393
- return false;
394
- }
395
- for (const child of children) {
396
- if (!this.isEmpty(child.children || [])) {
397
- return false;
375
+ static isEmpty(node: MenuNode): boolean {
376
+ if (CompoundMenuNode.is(node)) {
377
+ if (node.children.length === 0) {
378
+ return true;
398
379
  }
380
+ for (const child of node.children) {
381
+ if (!MenuModelRegistry.isEmpty(child)) {
382
+ return false;
383
+ }
384
+ }
385
+ } else {
386
+ return false;
399
387
  }
400
388
  return true;
401
389
  }
402
390
 
403
- protected changeEventOnDispose(path: MenuPath, id: string, disposable: Disposable): Disposable {
404
- return Disposable.create(() => {
405
- disposable.dispose();
406
- this.fireChangeEvent({
407
- path,
408
- affectedChildId: id,
409
- kind: ChangeKind.REMOVED
410
- });
411
- });
412
- }
413
-
414
391
  protected fireChangeEvent<T extends MenuChangedEvent>(evt: T): void {
415
392
  if (this.isReady) {
416
393
  this.onDidChangeEmitter.fire(evt);
417
394
  }
418
395
  }
419
-
420
- /**
421
- * Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined.
422
- * Returns `undefined` if the `parent` of any node in the chain is unknown.
423
- */
424
- getPath(node: MenuNode): MenuPath | undefined {
425
- const identifiers = [];
426
- const visited: MenuNode[] = [];
427
- let next: MenuNode | undefined = node;
428
-
429
- while (next && !visited.includes(next)) {
430
- if (next === this.root) {
431
- return identifiers.reverse();
432
- }
433
- visited.push(next);
434
- identifiers.push(next.id);
435
- next = next.parent;
436
- }
437
- return undefined;
438
- }
439
396
  }