@theia/core 1.62.0-next.3 → 1.62.1

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 +206 -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
@@ -18,8 +18,8 @@ import { injectable, inject } from 'inversify';
18
18
  import { Menu, MenuBar, Menu as MenuWidget, Widget } from '@lumino/widgets';
19
19
  import { CommandRegistry as LuminoCommandRegistry } from '@lumino/commands';
20
20
  import {
21
- CommandRegistry, environment, DisposableCollection, Disposable,
22
- MenuModelRegistry, MAIN_MENU_BAR, MenuPath, MenuNode, MenuCommandExecutor, CompoundMenuNode, CompoundMenuNodeRole, CommandMenuNode,
21
+ environment, DisposableCollection,
22
+ AcceleratorSource,
23
23
  ArrayUtils
24
24
  } from '../../common';
25
25
  import { KeybindingRegistry } from '../keybinding';
@@ -32,6 +32,8 @@ import { ApplicationShell } from '../shell';
32
32
  import { CorePreferences } from '../core-preferences';
33
33
  import { PreferenceService } from '../preferences/preference-service';
34
34
  import { ElementExt } from '@lumino/domutils';
35
+ import { CommandMenu, CompoundMenuNode, MAIN_MENU_BAR, MenuNode, MenuPath, RenderedMenuNode, Submenu } from '../../common/menu/menu-types';
36
+ import { MenuModelRegistry } from '../../common/menu/menu-model-registry';
35
37
 
36
38
  export abstract class MenuBarWidget extends MenuBar {
37
39
  abstract activateMenu(label: string, ...labels: string[]): Promise<MenuWidget>;
@@ -39,10 +41,7 @@ export abstract class MenuBarWidget extends MenuBar {
39
41
  }
40
42
 
41
43
  export interface BrowserMenuOptions extends MenuWidget.IOptions {
42
- commands: MenuCommandRegistry,
43
44
  context?: HTMLElement,
44
- contextKeyService?: ContextMatcher;
45
- rootMenuPath: MenuPath
46
45
  };
47
46
 
48
47
  @injectable()
@@ -54,12 +53,6 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
54
53
  @inject(ContextMenuContext)
55
54
  protected readonly context: ContextMenuContext;
56
55
 
57
- @inject(CommandRegistry)
58
- protected readonly commandRegistry: CommandRegistry;
59
-
60
- @inject(MenuCommandExecutor)
61
- protected readonly menuCommandExecutor: MenuCommandExecutor;
62
-
63
56
  @inject(CorePreferences)
64
57
  protected readonly corePreferences: CorePreferences;
65
58
 
@@ -108,53 +101,31 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
108
101
  }
109
102
 
110
103
  protected fillMenuBar(menuBar: MenuBarWidget): void {
111
- const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
112
- const menuCommandRegistry = this.createMenuCommandRegistry(menuModel);
104
+ const menuModel = this.menuProvider.getMenuNode(MAIN_MENU_BAR) as Submenu;
105
+ const menuCommandRegistry = new LuminoCommandRegistry();
113
106
  for (const menu of menuModel.children) {
114
- if (CompoundMenuNode.is(menu)) {
115
- const menuWidget = this.createMenuWidget(menu, { commands: menuCommandRegistry, rootMenuPath: MAIN_MENU_BAR });
107
+ if (CompoundMenuNode.is(menu) && RenderedMenuNode.is(menu)) {
108
+ const menuWidget = this.createMenuWidget(MAIN_MENU_BAR, menu, this.contextKeyService, { commands: menuCommandRegistry });
116
109
  menuBar.addMenu(menuWidget);
117
110
  }
118
111
  }
119
112
  }
120
113
 
121
- createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuWidget {
122
- const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(path), path) : this.menuProvider.getMenu(path);
123
- const menuCommandRegistry = this.createMenuCommandRegistry(menuModel, args).snapshot(path);
124
- const contextMenu = this.createMenuWidget(menuModel, { commands: menuCommandRegistry, context, rootMenuPath: path, contextKeyService });
114
+ createContextMenu(effectiveMenuPath: MenuPath, menuModel: CompoundMenuNode, contextMatcher: ContextMatcher, args?: unknown[], context?: HTMLElement): MenuWidget {
115
+ const menuCommandRegistry = new LuminoCommandRegistry();
116
+ const contextMenu = this.createMenuWidget(effectiveMenuPath, menuModel, contextMatcher, { commands: menuCommandRegistry, context }, args);
125
117
  return contextMenu;
126
118
  }
127
119
 
128
- createMenuWidget(menu: CompoundMenuNode, options: BrowserMenuOptions): DynamicMenuWidget {
129
- return new DynamicMenuWidget(menu, options, this.services);
130
- }
131
-
132
- protected createMenuCommandRegistry(menu: CompoundMenuNode, args: unknown[] = []): MenuCommandRegistry {
133
- const menuCommandRegistry = new MenuCommandRegistry(this.services);
134
- this.registerMenu(menuCommandRegistry, menu, args);
135
- return menuCommandRegistry;
136
- }
137
-
138
- protected registerMenu(menuCommandRegistry: MenuCommandRegistry, menu: MenuNode, args: unknown[]): void {
139
- if (CompoundMenuNode.is(menu)) {
140
- menu.children.forEach(child => this.registerMenu(menuCommandRegistry, child, args));
141
- } else if (CommandMenuNode.is(menu)) {
142
- menuCommandRegistry.registerActionMenu(menu, args);
143
- if (CommandMenuNode.hasAltHandler(menu)) {
144
- menuCommandRegistry.registerActionMenu(menu.altNode, args);
145
- }
146
-
147
- }
120
+ createMenuWidget(parentPath: MenuPath, menu: CompoundMenuNode, contextMatcher: ContextMatcher, options: BrowserMenuOptions, args?: unknown[]): DynamicMenuWidget {
121
+ return new DynamicMenuWidget(parentPath, menu, options, contextMatcher, this.services, args);
148
122
  }
149
123
 
150
124
  protected get services(): MenuServices {
151
125
  return {
152
- context: this.context,
153
126
  contextKeyService: this.contextKeyService,
154
- commandRegistry: this.commandRegistry,
155
- keybindingRegistry: this.keybindingRegistry,
127
+ context: this.context,
156
128
  menuWidgetFactory: this,
157
- commandExecutor: this.menuCommandExecutor,
158
129
  };
159
130
  }
160
131
 
@@ -235,41 +206,43 @@ export class DynamicMenuBarWidget extends MenuBarWidget {
235
206
  }
236
207
 
237
208
  export class MenuServices {
238
- readonly commandRegistry: CommandRegistry;
239
- readonly keybindingRegistry: KeybindingRegistry;
240
209
  readonly contextKeyService: ContextKeyService;
241
210
  readonly context: ContextMenuContext;
242
211
  readonly menuWidgetFactory: MenuWidgetFactory;
243
- readonly commandExecutor: MenuCommandExecutor;
244
212
  }
245
213
 
246
214
  export interface MenuWidgetFactory {
247
- createMenuWidget(menu: MenuNode & Required<Pick<MenuNode, 'children'>>, options: BrowserMenuOptions): MenuWidget;
215
+ createMenuWidget(effectiveMenuPath: MenuPath, menu: Submenu, contextMatcher: ContextMatcher, options: BrowserMenuOptions): MenuWidget;
248
216
  }
249
217
 
250
218
  /**
251
219
  * A menu widget that would recompute its items on update.
252
220
  */
253
221
  export class DynamicMenuWidget extends MenuWidget {
254
-
222
+ private static nextCommmandId = 0;
255
223
  /**
256
224
  * We want to restore the focus after the menu closes.
257
225
  */
258
226
  protected previousFocusedElement: HTMLElement | undefined;
259
227
 
260
228
  constructor(
229
+ protected readonly effectiveMenuPath: MenuPath,
261
230
  protected menu: CompoundMenuNode,
262
231
  protected options: BrowserMenuOptions,
263
- protected services: MenuServices
232
+ protected contextMatcher: ContextMatcher,
233
+ protected services: MenuServices,
234
+ protected args?: unknown[]
264
235
  ) {
265
236
  super(options);
266
- if (menu.label) {
267
- this.title.label = menu.label;
268
- }
269
- if (menu.icon) {
270
- this.title.iconClass = menu.icon;
237
+ if (RenderedMenuNode.is(this.menu)) {
238
+ if (this.menu.label) {
239
+ this.title.label = this.menu.label;
240
+ }
241
+ if (this.menu.icon) {
242
+ this.title.iconClass = this.menu.icon;
243
+ }
271
244
  }
272
- this.updateSubMenus(this, this.menu, this.options.commands);
245
+ this.updateSubMenus(this.effectiveMenuPath, this, this.menu, this.options.commands, this.contextMatcher, this.options.context);
273
246
  }
274
247
 
275
248
  protected override onAfterAttach(msg: Message): void {
@@ -318,8 +291,7 @@ export class DynamicMenuWidget extends MenuWidget {
318
291
  this.preserveFocusedElement(previousFocusedElement);
319
292
  this.clearItems();
320
293
  this.runWithPreservedFocusContext(() => {
321
- this.options.commands.snapshot(this.options.rootMenuPath);
322
- this.updateSubMenus(this, this.menu, this.options.commands);
294
+ this.updateSubMenus(this.effectiveMenuPath, this, this.menu, this.options.commands, this.contextMatcher, this.options.context);
323
295
  });
324
296
  }
325
297
 
@@ -333,8 +305,9 @@ export class DynamicMenuWidget extends MenuWidget {
333
305
  super.open(x, y, options);
334
306
  }
335
307
 
336
- protected updateSubMenus(parent: MenuWidget, menu: CompoundMenuNode, commands: MenuCommandRegistry): void {
337
- const items = this.buildSubMenus([], menu, commands);
308
+ protected updateSubMenus(parentPath: MenuPath, parent: MenuWidget, menu: CompoundMenuNode, commands: LuminoCommandRegistry,
309
+ contextMatcher: ContextMatcher, context?: HTMLElement | undefined): void {
310
+ const items = this.createItems(parentPath, menu.children, commands, contextMatcher, context);
338
311
  while (items[items.length - 1]?.type === 'separator') {
339
312
  items.pop();
340
313
  }
@@ -350,43 +323,58 @@ export class DynamicMenuWidget extends MenuWidget {
350
323
  }
351
324
  }
352
325
 
353
- protected buildSubMenus(parentItems: MenuWidget.IItemOptions[], menu: MenuNode, commands: MenuCommandRegistry): MenuWidget.IItemOptions[] {
354
- if (CompoundMenuNode.is(menu)
355
- && menu.children.length
356
- && this.undefinedOrMatch(this.options.contextKeyService ?? this.services.contextKeyService, menu.when, this.options.context)) {
357
- const role = menu === this.menu ? CompoundMenuNodeRole.Group : CompoundMenuNode.getRole(menu);
358
- if (role === CompoundMenuNodeRole.Submenu) {
359
- const submenu = this.services.menuWidgetFactory.createMenuWidget(menu, this.options);
360
- if (submenu.items.length > 0) {
361
- parentItems.push({ type: 'submenu', submenu });
362
- }
363
- } else if (role === CompoundMenuNodeRole.Group && menu.id !== 'inline') {
364
- const children = CompoundMenuNode.getFlatChildren(menu.children);
365
- const myItems: MenuWidget.IItemOptions[] = [];
366
- children.forEach(child => this.buildSubMenus(myItems, child, commands));
367
- if (myItems.length) {
368
- if (parentItems.length && parentItems[parentItems.length - 1].type !== 'separator') {
369
- parentItems.push({ type: 'separator' });
326
+ protected createItems(parentPath: MenuPath, nodes: MenuNode[], phCommandRegistry: LuminoCommandRegistry,
327
+ contextMatcher: ContextMatcher, context?: HTMLElement): MenuWidget.IItemOptions[] {
328
+ const result: MenuWidget.IItemOptions[] = [];
329
+
330
+ for (const node of nodes) {
331
+ const nodePath = [...parentPath, node.id];
332
+ if (node.isVisible(nodePath, contextMatcher, context, ...(this.args || []))) {
333
+ if (CompoundMenuNode.is(node)) {
334
+ if (RenderedMenuNode.is(node)) {
335
+ const submenu = this.services.menuWidgetFactory.createMenuWidget(nodePath, node, this.contextMatcher, this.options);
336
+ if (submenu.items.length > 0) {
337
+ result.push({ type: 'submenu', submenu });
338
+ }
339
+ } else {
340
+ const items = this.createItems(nodePath, node.children, phCommandRegistry, contextMatcher, context);
341
+ if (items.length > 0) {
342
+ if (node.id !== 'inline') {
343
+ result.push({ type: 'separator' });
344
+ }
345
+ result.push(...items);
346
+ if (node.id !== 'inline') {
347
+ result.push({ type: 'separator' });
348
+ }
349
+ }
350
+ }
351
+ } else if (CommandMenu.is(node)) {
352
+ const id = !phCommandRegistry.hasCommand(node.id) ? node.id : `${node.id}:${DynamicMenuWidget.nextCommmandId++}`;
353
+ phCommandRegistry.addCommand(id, {
354
+ execute: () => { node.run(nodePath, ...(this.args || [])); },
355
+ isEnabled: () => node.isEnabled(nodePath, ...(this.args || [])),
356
+ isToggled: () => node.isToggled ? !!node.isToggled(nodePath, ...(this.args || [])) : false,
357
+ isVisible: () => true,
358
+ label: node.label,
359
+ iconClass: node.icon,
360
+ });
361
+
362
+ const accelerator = (AcceleratorSource.is(node) ? node.getAccelerator(this.options.context) : []);
363
+ if (accelerator.length > 0) {
364
+ phCommandRegistry.addKeyBinding({
365
+ command: id,
366
+ keys: accelerator,
367
+ selector: '.p-Widget' // We have the PhosphorJS dependency anyway.
368
+ });
370
369
  }
371
- parentItems.push(...myItems);
372
- parentItems.push({ type: 'separator' });
370
+ result.push({
371
+ command: id,
372
+ type: 'command'
373
+ });
373
374
  }
374
375
  }
375
- } else if (menu.command) {
376
- const node = menu.altNode && this.services.context.altPressed ? menu.altNode : (menu as MenuNode & CommandMenuNode);
377
- if (commands.isVisible(node.command) && this.undefinedOrMatch(this.options.contextKeyService ?? this.services.contextKeyService, node.when, this.options.context)) {
378
- parentItems.push({
379
- command: node.command,
380
- type: 'command'
381
- });
382
- }
383
376
  }
384
- return parentItems;
385
- }
386
-
387
- protected undefinedOrMatch(contextKeyService: ContextMatcher, expression?: string, context?: HTMLElement): boolean {
388
- if (expression) { return contextKeyService.match(expression, context); }
389
- return true;
377
+ return result;
390
378
  }
391
379
 
392
380
  protected preserveFocusedElement(previousFocusedElement: Element | null = document.activeElement): boolean {
@@ -473,79 +461,3 @@ export class BrowserMenuBarContribution implements FrontendApplicationContributi
473
461
  return logo;
474
462
  }
475
463
  }
476
-
477
- /**
478
- * Stores Theia-specific action menu nodes instead of Lumino commands with their handlers.
479
- */
480
- export class MenuCommandRegistry extends LuminoCommandRegistry {
481
-
482
- protected actions = new Map<string, [MenuNode & CommandMenuNode, unknown[]]>();
483
- protected toDispose = new DisposableCollection();
484
-
485
- constructor(protected services: MenuServices) {
486
- super();
487
- }
488
-
489
- registerActionMenu(menu: MenuNode & CommandMenuNode, args: unknown[]): void {
490
- const { commandRegistry } = this.services;
491
- const command = commandRegistry.getCommand(menu.command);
492
- if (!command) {
493
- return;
494
- }
495
- const { id } = command;
496
- if (this.actions.has(id)) {
497
- return;
498
- }
499
- this.actions.set(id, [menu, args]);
500
- }
501
-
502
- snapshot(menuPath: MenuPath): this {
503
- this.toDispose.dispose();
504
- for (const [menu, args] of this.actions.values()) {
505
- this.toDispose.push(this.registerCommand(menu, args, menuPath));
506
- }
507
- return this;
508
- }
509
-
510
- protected registerCommand(menu: MenuNode & CommandMenuNode, args: unknown[], menuPath: MenuPath): Disposable {
511
- const { commandRegistry, keybindingRegistry, commandExecutor } = this.services;
512
- const command = commandRegistry.getCommand(menu.command);
513
- if (!command) {
514
- return Disposable.NULL;
515
- }
516
- const { id } = command;
517
- if (this.hasCommand(id)) {
518
- // several menu items can be registered for the same command in different contexts
519
- return Disposable.NULL;
520
- }
521
-
522
- // We freeze the `isEnabled`, `isVisible`, and `isToggled` states so they won't change.
523
- const enabled = commandExecutor.isEnabled(menuPath, id, ...args);
524
- const visible = commandExecutor.isVisible(menuPath, id, ...args);
525
- const toggled = commandExecutor.isToggled(menuPath, id, ...args);
526
- const unregisterCommand = this.addCommand(id, {
527
- execute: () => commandExecutor.executeCommand(menuPath, id, ...args),
528
- label: menu.label,
529
- iconClass: menu.icon,
530
- isEnabled: () => enabled,
531
- isVisible: () => visible,
532
- isToggled: () => toggled
533
- });
534
-
535
- const bindings = keybindingRegistry.getKeybindingsForCommand(id);
536
- // Only consider the first active keybinding.
537
- if (bindings.length) {
538
- const binding = bindings.length > 1 ?
539
- bindings.find(b => !b.when || this.services.contextKeyService.match(b.when)) ?? bindings[0] :
540
- bindings[0];
541
- const keys = keybindingRegistry.acceleratorFor(binding, ' ', true);
542
- this.addKeyBinding({
543
- command: id,
544
- keys,
545
- selector: '.lm-Widget' // We have the Lumino dependency anyway.
546
- });
547
- }
548
- return Disposable.create(() => unregisterCommand.dispose());
549
- }
550
-
551
- }
@@ -0,0 +1,140 @@
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-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { CompoundMenuNode, ContextExpressionMatcher, Group, MenuNode, MenuPath, Submenu } from '../../common/menu/menu-types';
18
+ import { Event } from '../../common';
19
+
20
+ export class SubMenuLink implements CompoundMenuNode {
21
+ constructor(private readonly delegate: Submenu, private readonly _sortString?: string, private readonly _when?: string) { }
22
+
23
+ get id(): string { return this.delegate.id; };
24
+ get onDidChange(): Event<void> | undefined { return this.delegate.onDidChange; };
25
+ get children(): MenuNode[] { return this.delegate.children; }
26
+ get contextKeyOverlays(): Record<string, string> | undefined { return this.delegate.contextKeyOverlays; }
27
+ get label(): string { return this.delegate.label; };
28
+ get icon(): string | undefined { return this.delegate.icon; };
29
+
30
+ get sortString(): string { return this._sortString || this.delegate.sortString; };
31
+ isVisible<T>(effectiveMenuPath: MenuPath, contextMatcher: ContextExpressionMatcher<T>, context: T | undefined, ...args: unknown[]): boolean {
32
+ return this.delegate.isVisible(effectiveMenuPath, contextMatcher, context) && this._when ? contextMatcher.match(this._when, context) : true;
33
+ }
34
+
35
+ isEmpty<T>(effectiveMenuPath: MenuPath, contextMatcher: ContextExpressionMatcher<T>, context: T | undefined, ...args: unknown[]): boolean {
36
+ return this.delegate.isEmpty(effectiveMenuPath, contextMatcher, context, args);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Node representing a (sub)menu in the menu tree structure.
42
+ */
43
+ export abstract class AbstractCompoundMenuImpl implements MenuNode {
44
+ readonly children: MenuNode[] = [];
45
+
46
+ protected constructor(
47
+ readonly id: string,
48
+ protected readonly orderString?: string,
49
+ protected readonly when?: string
50
+ ) {
51
+ }
52
+
53
+ getOrCreate(menuPath: MenuPath, pathIndex: number, endIndex: number): CompoundMenuImpl {
54
+ if (pathIndex === endIndex) {
55
+ return this;
56
+ }
57
+ let child = this.getNode(menuPath[pathIndex]);
58
+ if (!child) {
59
+ child = new GroupImpl(menuPath[pathIndex]);
60
+ this.addNode(child);
61
+ }
62
+ if (child instanceof AbstractCompoundMenuImpl) {
63
+ return child.getOrCreate(menuPath, pathIndex + 1, endIndex);
64
+ } else {
65
+ throw new Error(`An item exists, but it's not a parent: ${menuPath} at ${pathIndex}`);
66
+ }
67
+
68
+ }
69
+
70
+ /**
71
+ * Menu nodes are sorted in ascending order based on their `sortString`.
72
+ */
73
+ isVisible<T>(effectiveMenuPath: MenuPath, contextMatcher: ContextExpressionMatcher<T>, context: T | undefined, ...args: unknown[]): boolean {
74
+ return (!this.when || contextMatcher.match(this.when, context));
75
+ }
76
+
77
+ isEmpty<T>(effectiveMenuPath: MenuPath, contextMatcher: ContextExpressionMatcher<T>, context: T | undefined, ...args: unknown[]): boolean {
78
+ for (const child of this.children) {
79
+ if (child.isVisible(effectiveMenuPath, contextMatcher, context, args)) {
80
+ if (!CompoundMenuNode.is(child) || !child.isEmpty(effectiveMenuPath, contextMatcher, context, args)) {
81
+ return false;
82
+ }
83
+ }
84
+ }
85
+ return true;
86
+ }
87
+
88
+ addNode(...node: MenuNode[]): void {
89
+ this.children.push(...node);
90
+ this.children.sort(CompoundMenuNode.sortChildren);
91
+ }
92
+
93
+ getNode(id: string): MenuNode | undefined {
94
+ return this.children.find(node => node.id === id);
95
+ }
96
+
97
+ removeById(id: string): void {
98
+ const idx = this.children.findIndex(node => node.id === id);
99
+ if (idx >= 0) {
100
+ this.children.splice(idx, 1);
101
+ }
102
+ }
103
+
104
+ removeNode(node: MenuNode): void {
105
+ const idx = this.children.indexOf(node);
106
+ if (idx >= 0) {
107
+ this.children.splice(idx, 1);
108
+ }
109
+ }
110
+
111
+ get sortString(): string {
112
+ return this.orderString || this.id;
113
+ }
114
+ }
115
+
116
+ export class GroupImpl extends AbstractCompoundMenuImpl implements Group {
117
+ constructor(
118
+ id: string,
119
+ orderString?: string,
120
+ when?: string
121
+ ) {
122
+ super(id, orderString, when);
123
+ }
124
+ }
125
+
126
+ export class SubmenuImpl extends AbstractCompoundMenuImpl implements Submenu {
127
+
128
+ constructor(
129
+ id: string,
130
+ readonly label: string,
131
+ readonly contextKeyOverlays: Record<string, string> | undefined,
132
+ orderString?: string,
133
+ readonly icon?: string,
134
+ when?: string,
135
+ ) {
136
+ super(id, orderString, when);
137
+ }
138
+ }
139
+
140
+ export type CompoundMenuImpl = SubmenuImpl | GroupImpl;
@@ -15,12 +15,40 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import * as chai from 'chai';
18
- import { CommandContribution, CommandRegistry } from '../command';
19
- import { CompositeMenuNode } from './composite-menu-node';
20
- import { MenuContribution, MenuModelRegistry } from './menu-model-registry';
18
+ import {
19
+ CommandContribution, CommandMenu, CommandRegistry, CompoundMenuNode, Group, GroupImpl, MenuAction, MenuContribution,
20
+ MenuModelRegistry, MenuNode, MenuNodeFactory, MutableCompoundMenuNode, Submenu,
21
+ SubmenuImpl,
22
+ SubMenuLink
23
+ } from '../../common';
21
24
 
22
25
  const expect = chai.expect;
23
26
 
27
+ class TestMenuNodeFactory implements MenuNodeFactory {
28
+ createGroup(id: string, orderString?: string, when?: string): Group & MutableCompoundMenuNode {
29
+ return new GroupImpl(id, orderString, when);
30
+ }
31
+ createSubmenu(id: string, label: string, contextKeyOverlays: Record<string, string> | undefined, orderString?: string, icon?: string, when?: string):
32
+ Submenu & MutableCompoundMenuNode {
33
+ return new SubmenuImpl(id, label, contextKeyOverlays, orderString, icon, when);
34
+ }
35
+ createSubmenuLink(delegate: Submenu, sortString?: string, when?: string): MenuNode {
36
+ return new SubMenuLink(delegate, sortString, when);
37
+ }
38
+
39
+ createCommandMenu(item: MenuAction): CommandMenu {
40
+ return {
41
+ isVisible: () => true,
42
+ isEnabled: () => true,
43
+ isToggled: () => false,
44
+ id: item.commandId,
45
+ label: item.label || '',
46
+ sortString: item.order || '',
47
+ run: () => Promise.resolve()
48
+ };
49
+ }
50
+ }
51
+
24
52
  describe('menu-model-registry', () => {
25
53
 
26
54
  describe('01 #register', () => {
@@ -49,15 +77,13 @@ describe('menu-model-registry', () => {
49
77
  });
50
78
  }
51
79
  });
52
- const all = service.getMenu();
53
- const main = all.children[0] as CompositeMenuNode;
80
+ const main = service.getMenu(['main'])!;
54
81
  expect(main.children.length).equals(1);
55
82
  expect(main.id, 'main');
56
- expect(all.children.length).equals(1);
57
- const file = main.children[0] as CompositeMenuNode;
83
+ const file = main.children[0] as Submenu;
58
84
  expect(file.children.length).equals(1);
59
85
  expect(file.label, 'File');
60
- const openGroup = file.children[0] as CompositeMenuNode;
86
+ const openGroup = file.children[0] as Submenu;
61
87
  expect(openGroup.children.length).equals(2);
62
88
  expect(openGroup.label).undefined;
63
89
  });
@@ -69,16 +95,22 @@ describe('menu-model-registry', () => {
69
95
  const service = createMenuRegistry({
70
96
  registerMenus(menuRegistry: MenuModelRegistry): void {
71
97
  menuRegistry.registerSubmenu(fileMenu, 'File');
98
+ menuRegistry.registerSubmenu(fileOpenMenu, 'Open');
99
+ menuRegistry.registerSubmenu(fileCloseMenu, 'Close');
72
100
  // open menu should not be added to open menu
73
- menuRegistry.linkSubmenu(fileOpenMenu, fileOpenMenu);
101
+ try {
102
+ menuRegistry.linkCompoundMenuNode({ newParentPath: fileOpenMenu, submenuPath: fileOpenMenu });
103
+ } catch (e) {
104
+ // expected
105
+ }
74
106
  // close menu should be added
75
- menuRegistry.linkSubmenu(fileOpenMenu, fileCloseMenu);
107
+ menuRegistry.linkCompoundMenuNode({ newParentPath: fileOpenMenu, submenuPath: fileCloseMenu });
76
108
  }
77
109
  }, {
78
110
  registerCommands(reg: CommandRegistry): void { }
79
111
  });
80
- const all = service.getMenu() as CompositeMenuNode;
81
- expect(menuStructureToString(all.children[0] as CompositeMenuNode)).equals('File(0_open(1_close),1_close())');
112
+ const main = service.getMenu(['main']) as CompoundMenuNode;
113
+ expect(menuStructureToString(main)).equals('File(0_open(1_close()),1_close())');
82
114
  });
83
115
  });
84
116
  });
@@ -86,14 +118,14 @@ describe('menu-model-registry', () => {
86
118
  function createMenuRegistry(menuContrib: MenuContribution, commandContrib: CommandContribution): MenuModelRegistry {
87
119
  const cmdReg = new CommandRegistry({ getContributions: () => [commandContrib] });
88
120
  cmdReg.onStart();
89
- const menuReg = new MenuModelRegistry({ getContributions: () => [menuContrib] }, cmdReg);
121
+ const menuReg = new MenuModelRegistry({ getContributions: () => [menuContrib] }, cmdReg, new TestMenuNodeFactory());
90
122
  menuReg.onStart();
91
123
  return menuReg;
92
124
  }
93
125
 
94
- function menuStructureToString(node: CompositeMenuNode): string {
126
+ function menuStructureToString(node: CompoundMenuNode): string {
95
127
  return node.children.map(c => {
96
- if (c instanceof CompositeMenuNode) {
128
+ if (CompoundMenuNode.is(c)) {
97
129
  return `${c.id}(${menuStructureToString(c)})`;
98
130
  }
99
131
  return c.id;
@@ -49,7 +49,7 @@ export interface OpenWithHandler {
49
49
  */
50
50
  canHandle(uri: URI): number;
51
51
  /**
52
- * Test whether this handler and open the given URI
52
+ * Test whether this handler can open the given URI
53
53
  * and return the order of this handler in the list.
54
54
  */
55
55
  getOrder?(uri: URI): number;
@@ -222,7 +222,12 @@ export class SaveableService implements FrontendApplicationContribution {
222
222
  if (!Saveable.isDirty(widget)) {
223
223
  return false;
224
224
  }
225
- if (this.autoSave !== 'off') {
225
+ const saveable = Saveable.get(widget);
226
+ if (!saveable) {
227
+ console.warn('Saveable.get returned undefined on a known saveable widget. This is unexpected.');
228
+ }
229
+ // Enter branch if saveable absent since we cannot check autosaveability more definitely.
230
+ if (this.autoSave !== 'off' && (!saveable || this.shouldAutoSave(widget, saveable))) {
226
231
  return true;
227
232
  }
228
233
  const notLastWithDocument = !Saveable.closingWidgetWouldLoseSaveable(widget, Array.from(this.saveThrottles.keys()));