@theia/core 1.70.0-next.7 → 1.70.0-next.81

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 (236) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/about-dialog.d.ts.map +1 -1
  3. package/lib/browser/about-dialog.js +3 -1
  4. package/lib/browser/about-dialog.js.map +1 -1
  5. package/lib/browser/catalog.json +83 -7
  6. package/lib/browser/common-frontend-contribution.d.ts +0 -2
  7. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  8. package/lib/browser/common-frontend-contribution.js +39 -15
  9. package/lib/browser/common-frontend-contribution.js.map +1 -1
  10. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  11. package/lib/browser/frontend-application-module.js +1 -0
  12. package/lib/browser/frontend-application-module.js.map +1 -1
  13. package/lib/browser/hover-service.d.ts.map +1 -1
  14. package/lib/browser/hover-service.js +5 -3
  15. package/lib/browser/hover-service.js.map +1 -1
  16. package/lib/browser/markdown-rendering/markdown-renderer.d.ts +1 -1
  17. package/lib/browser/markdown-rendering/markdown-renderer.d.ts.map +1 -1
  18. package/lib/browser/menu/action-menu-node.d.ts +1 -0
  19. package/lib/browser/menu/action-menu-node.d.ts.map +1 -1
  20. package/lib/browser/menu/action-menu-node.js +1 -0
  21. package/lib/browser/menu/action-menu-node.js.map +1 -1
  22. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  23. package/lib/browser/menu/browser-menu-plugin.js +9 -1
  24. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  25. package/lib/browser/menu/composite-menu-node.d.ts +2 -1
  26. package/lib/browser/menu/composite-menu-node.d.ts.map +1 -1
  27. package/lib/browser/menu/composite-menu-node.js +3 -1
  28. package/lib/browser/menu/composite-menu-node.js.map +1 -1
  29. package/lib/browser/menu/menu.spec.js +15 -0
  30. package/lib/browser/menu/menu.spec.js.map +1 -1
  31. package/lib/browser/menu/utils.d.ts +2 -0
  32. package/lib/browser/menu/utils.d.ts.map +1 -0
  33. package/lib/browser/menu/utils.js +29 -0
  34. package/lib/browser/menu/utils.js.map +1 -0
  35. package/lib/browser/quick-input/quick-input-service.spec.js +53 -9
  36. package/lib/browser/quick-input/quick-input-service.spec.js.map +1 -1
  37. package/lib/browser/saveable-service.d.ts +24 -1
  38. package/lib/browser/saveable-service.d.ts.map +1 -1
  39. package/lib/browser/saveable-service.js +34 -3
  40. package/lib/browser/saveable-service.js.map +1 -1
  41. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts +3 -0
  42. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -1
  43. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +10 -0
  44. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
  45. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +7 -0
  46. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -1
  47. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +35 -0
  48. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
  49. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +2 -1
  50. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  51. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +9 -3
  52. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  53. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts +2 -0
  54. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts.map +1 -1
  55. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js +3 -0
  56. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js.map +1 -1
  57. package/lib/browser/tree/fuzzy-search.d.ts +1 -60
  58. package/lib/browser/tree/fuzzy-search.d.ts.map +1 -1
  59. package/lib/browser/tree/fuzzy-search.js +3 -58
  60. package/lib/browser/tree/fuzzy-search.js.map +1 -1
  61. package/lib/browser/tree/tree-view-welcome-widget.d.ts.map +1 -1
  62. package/lib/browser/tree/tree-view-welcome-widget.js +1 -2
  63. package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
  64. package/lib/browser/widgets/select-component.d.ts +1 -0
  65. package/lib/browser/widgets/select-component.d.ts.map +1 -1
  66. package/lib/browser/widgets/select-component.js +30 -0
  67. package/lib/browser/widgets/select-component.js.map +1 -1
  68. package/lib/browser/window/default-window-service.d.ts +2 -1
  69. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  70. package/lib/browser/window/default-window-service.js +5 -1
  71. package/lib/browser/window/default-window-service.js.map +1 -1
  72. package/lib/browser/window/test/mock-window-service.d.ts +2 -1
  73. package/lib/browser/window/test/mock-window-service.d.ts.map +1 -1
  74. package/lib/browser/window/test/mock-window-service.js +2 -1
  75. package/lib/browser/window/test/mock-window-service.js.map +1 -1
  76. package/lib/browser/window/window-service.d.ts +8 -1
  77. package/lib/browser/window/window-service.d.ts.map +1 -1
  78. package/lib/common/fuzzy-match-utils.d.ts +27 -0
  79. package/lib/common/fuzzy-match-utils.d.ts.map +1 -0
  80. package/lib/common/fuzzy-match-utils.js +96 -0
  81. package/lib/common/fuzzy-match-utils.js.map +1 -0
  82. package/lib/common/fuzzy-match-utils.spec.d.ts +2 -0
  83. package/lib/common/fuzzy-match-utils.spec.d.ts.map +1 -0
  84. package/lib/common/fuzzy-match-utils.spec.js +109 -0
  85. package/lib/common/fuzzy-match-utils.spec.js.map +1 -0
  86. package/lib/common/fuzzy-search.d.ts +63 -0
  87. package/lib/common/fuzzy-search.d.ts.map +1 -0
  88. package/lib/common/fuzzy-search.js +101 -0
  89. package/lib/common/fuzzy-search.js.map +1 -0
  90. package/lib/common/fuzzy-search.spec.d.ts.map +1 -0
  91. package/lib/{browser/tree → common}/fuzzy-search.spec.js +20 -1
  92. package/lib/common/fuzzy-search.spec.js.map +1 -0
  93. package/lib/common/logger-sanitizer.d.ts +9 -0
  94. package/lib/common/logger-sanitizer.d.ts.map +1 -1
  95. package/lib/common/logger-sanitizer.js +26 -3
  96. package/lib/common/logger-sanitizer.js.map +1 -1
  97. package/lib/common/logger-sanitizer.spec.js +41 -0
  98. package/lib/common/logger-sanitizer.spec.js.map +1 -1
  99. package/lib/common/menu/menu-types.d.ts +4 -0
  100. package/lib/common/menu/menu-types.d.ts.map +1 -1
  101. package/lib/common/menu/menu-types.js.map +1 -1
  102. package/lib/common/preferences/injectable-preference-proxy.d.ts +5 -3
  103. package/lib/common/preferences/injectable-preference-proxy.d.ts.map +1 -1
  104. package/lib/common/preferences/injectable-preference-proxy.js +9 -6
  105. package/lib/common/preferences/injectable-preference-proxy.js.map +1 -1
  106. package/lib/common/preferences/preference-configurations.d.ts +2 -2
  107. package/lib/common/preferences/preference-configurations.d.ts.map +1 -1
  108. package/lib/common/preferences/preference-configurations.js +1 -1
  109. package/lib/common/preferences/preference-configurations.js.map +1 -1
  110. package/lib/common/preferences/preference-provider-impl.d.ts +4 -3
  111. package/lib/common/preferences/preference-provider-impl.d.ts.map +1 -1
  112. package/lib/common/preferences/preference-provider-impl.js +9 -6
  113. package/lib/common/preferences/preference-provider-impl.js.map +1 -1
  114. package/lib/common/preferences/preference-proxy.d.ts +4 -2
  115. package/lib/common/preferences/preference-proxy.d.ts.map +1 -1
  116. package/lib/common/preferences/preference-proxy.js +4 -5
  117. package/lib/common/preferences/preference-proxy.js.map +1 -1
  118. package/lib/common/preferences/preference-service.d.ts +4 -3
  119. package/lib/common/preferences/preference-service.d.ts.map +1 -1
  120. package/lib/common/preferences/preference-service.js +13 -10
  121. package/lib/common/preferences/preference-service.js.map +1 -1
  122. package/lib/common/quick-pick-service.d.ts +18 -2
  123. package/lib/common/quick-pick-service.d.ts.map +1 -1
  124. package/lib/common/quick-pick-service.js +53 -8
  125. package/lib/common/quick-pick-service.js.map +1 -1
  126. package/lib/common/uri.js +1 -1
  127. package/lib/common/uri.js.map +1 -1
  128. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  129. package/lib/electron-browser/menu/electron-main-menu-factory.js +4 -6
  130. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  131. package/lib/electron-browser/menu/electron-menu-contribution.js +3 -3
  132. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  133. package/lib/electron-browser/messaging/electron-local-ws-connection-source.d.ts +2 -0
  134. package/lib/electron-browser/messaging/electron-local-ws-connection-source.d.ts.map +1 -1
  135. package/lib/electron-browser/messaging/electron-local-ws-connection-source.js +5 -3
  136. package/lib/electron-browser/messaging/electron-local-ws-connection-source.js.map +1 -1
  137. package/lib/electron-browser/preload.js +2 -2
  138. package/lib/electron-browser/preload.js.map +1 -1
  139. package/lib/electron-browser/window/electron-secondary-window-service.d.ts +4 -0
  140. package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
  141. package/lib/electron-browser/window/electron-secondary-window-service.js +32 -0
  142. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  143. package/lib/electron-browser/window/electron-window-module.d.ts +1 -0
  144. package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
  145. package/lib/electron-browser/window/electron-window-module.js +4 -0
  146. package/lib/electron-browser/window/electron-window-module.js.map +1 -1
  147. package/lib/electron-browser/window/electron-window-service.d.ts +3 -2
  148. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  149. package/lib/electron-browser/window/electron-window-service.js +7 -4
  150. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  151. package/lib/electron-browser/window/window-zoom-action-bar.d.ts +21 -0
  152. package/lib/electron-browser/window/window-zoom-action-bar.d.ts.map +1 -0
  153. package/lib/electron-browser/window/window-zoom-action-bar.js +71 -0
  154. package/lib/electron-browser/window/window-zoom-action-bar.js.map +1 -0
  155. package/lib/electron-browser/window/window-zoom-status-bar-item.d.ts +14 -0
  156. package/lib/electron-browser/window/window-zoom-status-bar-item.d.ts.map +1 -0
  157. package/lib/electron-browser/window/window-zoom-status-bar-item.js +87 -0
  158. package/lib/electron-browser/window/window-zoom-status-bar-item.js.map +1 -0
  159. package/lib/electron-common/electron-api.d.ts +1 -1
  160. package/lib/electron-common/electron-api.d.ts.map +1 -1
  161. package/lib/electron-common/electron-main-window-service.d.ts +2 -1
  162. package/lib/electron-common/electron-main-window-service.d.ts.map +1 -1
  163. package/lib/electron-common/electron-window-preferences.d.ts +5 -2
  164. package/lib/electron-common/electron-window-preferences.d.ts.map +1 -1
  165. package/lib/electron-common/electron-window-preferences.js +16 -10
  166. package/lib/electron-common/electron-window-preferences.js.map +1 -1
  167. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  168. package/lib/electron-main/electron-api-main.js +14 -2
  169. package/lib/electron-main/electron-api-main.js.map +1 -1
  170. package/lib/electron-main/electron-main-application.d.ts +1 -0
  171. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  172. package/lib/electron-main/electron-main-application.js +6 -0
  173. package/lib/electron-main/electron-main-application.js.map +1 -1
  174. package/lib/electron-main/electron-main-window-service-impl.d.ts +2 -1
  175. package/lib/electron-main/electron-main-window-service-impl.d.ts.map +1 -1
  176. package/lib/electron-main/electron-main-window-service-impl.js +6 -2
  177. package/lib/electron-main/electron-main-window-service-impl.js.map +1 -1
  178. package/package.json +7 -7
  179. package/src/browser/about-dialog.tsx +3 -1
  180. package/src/browser/common-frontend-contribution.ts +36 -17
  181. package/src/browser/frontend-application-module.ts +2 -1
  182. package/src/browser/hover-service.ts +5 -3
  183. package/src/browser/markdown-rendering/markdown-renderer.ts +1 -1
  184. package/src/browser/menu/action-menu-node.ts +1 -0
  185. package/src/browser/menu/browser-menu-plugin.ts +9 -1
  186. package/src/browser/menu/composite-menu-node.ts +4 -2
  187. package/src/browser/menu/menu.spec.ts +17 -0
  188. package/src/browser/menu/utils.ts +26 -0
  189. package/src/browser/quick-input/quick-input-service.spec.ts +58 -9
  190. package/src/browser/saveable-service.ts +56 -4
  191. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +12 -0
  192. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +40 -1
  193. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +8 -4
  194. package/src/browser/shell/tab-bar-toolbar/tab-toolbar-item.tsx +4 -0
  195. package/src/browser/style/hover-service.css +1 -0
  196. package/src/browser/style/index.css +1 -1
  197. package/src/browser/tree/fuzzy-search.ts +2 -120
  198. package/src/browser/tree/tree-view-welcome-widget.tsx +1 -2
  199. package/src/browser/widgets/select-component.tsx +39 -2
  200. package/src/browser/window/default-window-service.ts +6 -1
  201. package/src/browser/window/test/mock-window-service.ts +2 -1
  202. package/src/browser/window/window-service.ts +9 -1
  203. package/src/common/fuzzy-match-utils.spec.ts +141 -0
  204. package/src/common/fuzzy-match-utils.ts +78 -0
  205. package/src/{browser/tree → common}/fuzzy-search.spec.ts +23 -1
  206. package/src/common/fuzzy-search.ts +158 -0
  207. package/src/common/i18n/nls.metadata.json +25379 -24577
  208. package/src/common/logger-sanitizer.spec.ts +54 -1
  209. package/src/common/logger-sanitizer.ts +38 -3
  210. package/src/common/menu/menu-types.ts +4 -0
  211. package/src/common/preferences/injectable-preference-proxy.ts +6 -3
  212. package/src/common/preferences/preference-configurations.ts +2 -2
  213. package/src/common/preferences/preference-provider-impl.ts +6 -3
  214. package/src/common/preferences/preference-proxy.ts +6 -4
  215. package/src/common/preferences/preference-service.ts +6 -3
  216. package/src/common/quick-pick-service.ts +65 -11
  217. package/src/common/uri.ts +1 -1
  218. package/src/electron-browser/menu/electron-main-menu-factory.ts +4 -6
  219. package/src/electron-browser/menu/electron-menu-contribution.ts +4 -4
  220. package/src/electron-browser/messaging/electron-local-ws-connection-source.ts +4 -2
  221. package/src/electron-browser/preload.ts +2 -2
  222. package/src/electron-browser/style/window-zoom-action-bar.css +57 -0
  223. package/src/electron-browser/window/electron-secondary-window-service.ts +32 -1
  224. package/src/electron-browser/window/electron-window-module.ts +4 -0
  225. package/src/electron-browser/window/electron-window-service.ts +10 -7
  226. package/src/electron-browser/window/window-zoom-action-bar.tsx +115 -0
  227. package/src/electron-browser/window/window-zoom-status-bar-item.ts +81 -0
  228. package/src/electron-common/electron-api.ts +1 -1
  229. package/src/electron-common/electron-main-window-service.ts +2 -1
  230. package/src/electron-common/electron-window-preferences.ts +19 -11
  231. package/src/electron-main/electron-api-main.ts +12 -2
  232. package/src/electron-main/electron-main-application.ts +7 -0
  233. package/src/electron-main/electron-main-window-service-impl.ts +7 -2
  234. package/lib/browser/tree/fuzzy-search.spec.d.ts.map +0 -1
  235. package/lib/browser/tree/fuzzy-search.spec.js.map +0 -1
  236. /package/lib/{browser/tree → common}/fuzzy-search.spec.d.ts +0 -0
@@ -15,8 +15,8 @@
15
15
  ********************************************************************************/
16
16
 
17
17
  import type { ApplicationShell } from './shell';
18
- import { injectable } from 'inversify';
19
- import { UNTITLED_SCHEME, URI, Disposable, DisposableCollection, Emitter, Event } from '../common';
18
+ import { inject, injectable, named } from 'inversify';
19
+ import { ContributionProvider, UNTITLED_SCHEME, URI, Disposable, DisposableCollection, Emitter, Event } from '../common';
20
20
  import { Navigatable, NavigatableWidget } from './navigatable-types';
21
21
  import { AutoSaveMode, Saveable, SaveableSource, SaveableWidget, SaveOptions, SaveReason, setDirty, close, PostCreationSaveableWidget, ShouldSaveDialog } from './saveable';
22
22
  import { waitForClosed, Widget } from './widgets';
@@ -24,16 +24,40 @@ import { FrontendApplicationContribution } from './frontend-application-contribu
24
24
  import { FrontendApplication } from './frontend-application';
25
25
  import throttle = require('lodash.throttle');
26
26
 
27
+ export const SaveErrorChecker = Symbol('SaveErrorChecker');
28
+
29
+ /**
30
+ * Contribution point for checking whether a given URI has errors.
31
+ * When `files.autoSaveWhenNoErrors` is enabled, auto-save will be suppressed
32
+ * for files where any registered checker reports errors.
33
+ */
34
+ export interface SaveErrorChecker {
35
+ /**
36
+ * Returns `true` if the given URI has errors that should prevent auto-save.
37
+ */
38
+ hasErrors(uri: URI): boolean;
39
+ /**
40
+ * Event fired when the error state may have changed (e.g. diagnostics updated).
41
+ * The SaveableService listens to this to re-evaluate auto-save for dirty widgets.
42
+ */
43
+ onDidErrorStateChange: Event<void>;
44
+ }
45
+
27
46
  @injectable()
28
47
  export class SaveableService implements FrontendApplicationContribution {
29
48
 
49
+ @inject(ContributionProvider) @named(SaveErrorChecker)
50
+ protected readonly errorCheckers: ContributionProvider<SaveErrorChecker>;
51
+
30
52
  protected saveThrottles = new Map<Widget, AutoSaveThrottle>();
31
53
  protected saveMode: AutoSaveMode = 'off';
32
54
  protected saveDelay = 1000;
55
+ protected saveWhenNoErrors = false;
33
56
  protected shell: ApplicationShell;
34
57
 
35
58
  protected readonly onDidAutoSaveChangeEmitter = new Emitter<AutoSaveMode>();
36
59
  protected readonly onDidAutoSaveDelayChangeEmitter = new Emitter<number>();
60
+ protected readonly onDidAutoSaveConditionsChangeEmitter = new Emitter<void>();
37
61
 
38
62
  get onDidAutoSaveChange(): Event<AutoSaveMode> {
39
63
  return this.onDidAutoSaveChangeEmitter.event;
@@ -43,6 +67,10 @@ export class SaveableService implements FrontendApplicationContribution {
43
67
  return this.onDidAutoSaveDelayChangeEmitter.event;
44
68
  }
45
69
 
70
+ get onDidAutoSaveConditionsChange(): Event<void> {
71
+ return this.onDidAutoSaveConditionsChangeEmitter.event;
72
+ }
73
+
46
74
  get autoSave(): AutoSaveMode {
47
75
  return this.saveMode;
48
76
  }
@@ -59,8 +87,22 @@ export class SaveableService implements FrontendApplicationContribution {
59
87
  this.updateAutoSaveDelay(value);
60
88
  }
61
89
 
90
+ get autoSaveWhenNoErrors(): boolean {
91
+ return this.saveWhenNoErrors;
92
+ }
93
+
94
+ set autoSaveWhenNoErrors(value: boolean) {
95
+ this.saveWhenNoErrors = value;
96
+ }
97
+
62
98
  onDidInitializeLayout(app: FrontendApplication): void {
63
99
  this.shell = app.shell;
100
+ // Listen to error state changes from all registered error checkers
101
+ for (const checker of this.errorCheckers.getContributions()) {
102
+ checker.onDidErrorStateChange(() => {
103
+ this.onDidAutoSaveConditionsChangeEmitter.fire();
104
+ });
105
+ }
64
106
  // Register restored editors first
65
107
  for (const widget of this.shell.widgets) {
66
108
  const saveable = Saveable.get(widget);
@@ -165,9 +207,16 @@ export class SaveableService implements FrontendApplicationContribution {
165
207
  if (uri?.scheme === UNTITLED_SCHEME) {
166
208
  // Never auto-save untitled documents
167
209
  return false;
168
- } else {
169
- return saveable.autosaveable !== false && saveable.dirty;
170
210
  }
211
+ if (saveable.autosaveable === false || !saveable.dirty) {
212
+ return false;
213
+ }
214
+ if (this.saveWhenNoErrors && uri) {
215
+ if (this.errorCheckers.getContributions().some(checker => checker.hasErrors(uri))) {
216
+ return false;
217
+ }
218
+ }
219
+ return true;
171
220
  }
172
221
 
173
222
  protected applySaveableWidget(widget: Widget, saveable: Saveable): void {
@@ -314,6 +363,9 @@ export class AutoSaveThrottle implements Disposable {
314
363
  }),
315
364
  saveService.onDidAutoSaveDelayChange(() => {
316
365
  this.throttledSave(true);
366
+ }),
367
+ saveService.onDidAutoSaveConditionsChange(() => {
368
+ this.throttledSave();
317
369
  })
318
370
  );
319
371
  }
@@ -24,6 +24,7 @@ import { ContextMenuRenderer } from '../../context-menu-renderer';
24
24
  import { TabBarToolbarItem } from './tab-toolbar-item';
25
25
  import { ContextKeyService, ContextMatcher } from '../../context-key-service';
26
26
  import { CommandMenu, CompoundMenuNode, ContextExpressionMatcher, Group, MenuModelRegistry, MenuNode, MenuPath, RenderedMenuNode, Submenu } from '../../../common/menu';
27
+ import { combineWhenExpressions } from '../../menu/utils';
27
28
 
28
29
  export const TOOLBAR_WRAPPER_ID_SUFFIX = '-as-tabbar-toolbar-item';
29
30
 
@@ -128,6 +129,9 @@ export class SubmenuAsToolbarItemWrapper extends AbstractToolbarMenuWrapper impl
128
129
  super(effectiveMenuPath, commandRegistry, menuRegistry, contextKeyService, contextMenuRenderer);
129
130
  }
130
131
  priority?: number | undefined;
132
+ get when(): string | undefined {
133
+ return this.menuNode.when;
134
+ }
131
135
 
132
136
  executeCommand(widget: Widget, e: React.MouseEvent<HTMLDivElement, MouseEvent>): void {
133
137
  }
@@ -164,6 +168,10 @@ export class CommandMenuAsToolbarItemWrapper extends AbstractToolbarMenuWrapper
164
168
  super(effectiveMenuPath, commandRegistry, menuRegistry, contextKeyService, contextMenuRenderer);
165
169
  }
166
170
 
171
+ get when(): string | undefined {
172
+ return this.menuNode.when;
173
+ }
174
+
167
175
  isVisible(widget: Widget): boolean {
168
176
  return this.menuNode.isVisible(this.effectiveMenuPath, this.contextKeyService, widget.node, widget);
169
177
  }
@@ -199,6 +207,10 @@ export class ToolbarActionWrapper extends AbstractToolbarMenuWrapper implements
199
207
  super(effectiveMenuPath, commandRegistry, menuRegistry, contextKeyService, contextMenuRenderer);
200
208
  }
201
209
 
210
+ get when(): string | undefined {
211
+ return combineWhenExpressions(this.toolbarItem.when, this.menuNode?.when);
212
+ }
213
+
202
214
  override isEnabled(widget?: Widget): boolean {
203
215
  return this.toolbarItem.command ? this.commandRegistry.isEnabled(this.toolbarItem.command, widget) : !!this.toolbarItem.menuPath;
204
216
  }
@@ -26,7 +26,7 @@ import { CommandMenuAsToolbarItemWrapper, SubmenuAsToolbarItemWrapper, ToolbarAc
26
26
  import { KeybindingRegistry } from '../../keybinding';
27
27
  import { LabelParser } from '../../label-parser';
28
28
  import { ContextMenuRenderer } from '../../context-menu-renderer';
29
- import { CommandMenu, CompoundMenuNode, RenderedMenuNode } from '../../../common/menu';
29
+ import { CommandMenu, CompoundMenuNode, MenuNode, RenderedMenuNode } from '../../../common/menu';
30
30
  import { ReactToolbarItemImpl, RenderedToolbarItemImpl, TabBarToolbarItem } from './tab-toolbar-item';
31
31
 
32
32
  /**
@@ -168,6 +168,45 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
168
168
  return result;
169
169
  }
170
170
 
171
+ /**
172
+ * Collects all context keys referenced by toolbar items and delegated menu nodes
173
+ * that may participate in rendering for the given widget.
174
+ */
175
+ collectContextKeys(widget: Widget): Set<string> {
176
+ const contextKeys = new Set<string>();
177
+ if (widget.isDisposed) {
178
+ return contextKeys;
179
+ }
180
+
181
+ for (const item of this.items.values()) {
182
+ if (item.when) {
183
+ this.contextKeyService.parseKeys(item.when)?.forEach(key => contextKeys.add(key));
184
+ }
185
+ }
186
+
187
+ for (const delegate of this.menuDelegates.values()) {
188
+ if (!delegate.isVisible(widget)) {
189
+ continue;
190
+ }
191
+ const menu = this.menuRegistry.getMenu(delegate.menuPath);
192
+ if (menu) {
193
+ this.collectMenuContextKeys(menu, contextKeys);
194
+ }
195
+ }
196
+ return contextKeys;
197
+ }
198
+
199
+ protected collectMenuContextKeys(menuNode: MenuNode, contextKeys: Set<string>): void {
200
+ if (menuNode.when) {
201
+ this.contextKeyService.parseKeys(menuNode.when)?.forEach(key => contextKeys.add(key));
202
+ }
203
+ if (CompoundMenuNode.is(menuNode)) {
204
+ for (const child of menuNode.children) {
205
+ this.collectMenuContextKeys(child, contextKeys);
206
+ }
207
+ }
208
+ }
209
+
171
210
  unregisterItem(id: string): void {
172
211
  if (this.items.delete(id)) {
173
212
  this.fireOnDidChange();
@@ -53,6 +53,7 @@ export class TabBarToolbar extends ReactWidget {
53
53
  protected contextKeyListener: Disposable | undefined;
54
54
  protected toDisposeOnUpdateItems: DisposableCollection = new DisposableCollection();
55
55
 
56
+ protected toolbarContextKeys = new Set<string>();
56
57
  protected keybindingContextKeys = new Set<string>();
57
58
 
58
59
  @inject(CommandRegistry) protected readonly commands: CommandRegistry;
@@ -74,7 +75,9 @@ export class TabBarToolbar extends ReactWidget {
74
75
  this.toDispose.pushAll([
75
76
  this.keybindings.onKeybindingsChanged(() => this.maybeUpdate()),
76
77
  this.contextKeyService.onDidChange(e => {
77
- if (e.affects(this.keybindingContextKeys)) {
78
+ if (this.current && e.affects(this.toolbarContextKeys)) {
79
+ this.updateTarget(this.current);
80
+ } else if (e.affects(this.keybindingContextKeys)) {
78
81
  this.maybeUpdate();
79
82
  }
80
83
  }),
@@ -85,11 +88,12 @@ export class TabBarToolbar extends ReactWidget {
85
88
  ]);
86
89
  }
87
90
 
88
- updateItems(items: Array<TabBarToolbarItem>, current: Widget | undefined): void {
91
+ updateItems(items: Array<TabBarToolbarItem>, current: Widget | undefined, contextKeys: Set<string> = new Set()): void {
89
92
  this.toDisposeOnUpdateItems.dispose();
90
93
  this.toDisposeOnUpdateItems = new DisposableCollection();
91
94
  this.inline.clear();
92
95
  this.more.clear();
96
+ this.toolbarContextKeys = contextKeys;
93
97
 
94
98
  for (const item of items.sort(TabBarToolbarAction.PRIORITY_COMPARATOR).reverse()) {
95
99
 
@@ -98,7 +102,6 @@ export class TabBarToolbar extends ReactWidget {
98
102
  } else {
99
103
  this.more.set(item.id, item);
100
104
  }
101
-
102
105
  if (item.onDidChange) {
103
106
  this.toDisposeOnUpdateItems.push(item.onDidChange(() => this.maybeUpdate()));
104
107
  }
@@ -116,7 +119,8 @@ export class TabBarToolbar extends ReactWidget {
116
119
  updateTarget(current?: Widget): void {
117
120
  const operativeWidget = TabBarDelegator.is(current) ? current.getTabBarDelegate() : current;
118
121
  const items = operativeWidget ? this.toolbarRegistry.visibleItems(operativeWidget) : [];
119
- this.updateItems(items, operativeWidget);
122
+ const contextKeys = operativeWidget ? this.toolbarRegistry.collectContextKeys(operativeWidget) : new Set<string>();
123
+ this.updateItems(items, operativeWidget, contextKeys);
120
124
  }
121
125
 
122
126
  protected readonly toDisposeOnSetCurrent = new DisposableCollection();
@@ -27,6 +27,7 @@ import { ActionMenuNode, GroupImpl, MenuNode } from '../../../common/menu';
27
27
 
28
28
  export interface TabBarToolbarItem {
29
29
  id: string;
30
+ when?: string;
30
31
  isVisible(widget: Widget): boolean;
31
32
  isEnabled(widget: Widget): boolean;
32
33
  isToggled(widget: Widget): boolean;
@@ -52,6 +53,9 @@ class AbstractToolbarItemImpl<T extends TabBarToolbarActionBase> {
52
53
  get id(): string {
53
54
  return this.action.id;
54
55
  }
56
+ get when(): string | undefined {
57
+ return this.action.when;
58
+ }
55
59
  get group(): string | undefined {
56
60
  return this.action.group;
57
61
  }
@@ -29,6 +29,7 @@
29
29
  border: 1px solid var(--theia-editorHoverWidget-border);
30
30
  padding: var(--theia-ui-padding);
31
31
  max-width: var(--theia-hover-max-width);
32
+ user-select: text;
32
33
  }
33
34
 
34
35
  /* overwrite potentially different default user agent styles */
@@ -78,13 +78,13 @@
78
78
 
79
79
  html,
80
80
  body {
81
+ overflow: hidden;
81
82
  height: 100vh;
82
83
  }
83
84
 
84
85
  body {
85
86
  margin: 0;
86
87
  padding: 0;
87
- overflow: hidden;
88
88
  font-family: var(--theia-ui-font-family);
89
89
  background: var(--theia-editor-background);
90
90
  color: var(--theia-foreground);
@@ -14,123 +14,5 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import * as fuzzy from 'fuzzy';
18
- import { injectable } from 'inversify';
19
-
20
- @injectable()
21
- export class FuzzySearch {
22
-
23
- private static readonly PRE = '\x01';
24
- private static readonly POST = '\x02';
25
-
26
- /**
27
- * Filters the input and returns with an array that contains all items that match the pattern.
28
- */
29
- async filter<T>(input: FuzzySearch.Input<T>): Promise<FuzzySearch.Match<T>[]> {
30
- return fuzzy.filter(input.pattern, input.items.slice(), {
31
- pre: FuzzySearch.PRE,
32
- post: FuzzySearch.POST,
33
- extract: input.transform
34
- }).sort(this.sortResults.bind(this)).map(this.mapResult.bind(this));
35
- }
36
-
37
- protected sortResults<T>(left: fuzzy.FilterResult<T>, right: fuzzy.FilterResult<T>): number {
38
- return left.index - right.index;
39
- }
40
-
41
- protected mapResult<T>(result: fuzzy.FilterResult<T>): FuzzySearch.Match<T> {
42
- return {
43
- item: result.original,
44
- ranges: this.mapRanges(result.string)
45
- };
46
- }
47
-
48
- protected mapRanges(input: string): ReadonlyArray<FuzzySearch.Range> {
49
- const copy = input.split('').filter(s => s !== '');
50
- const ranges: FuzzySearch.Range[] = [];
51
- const validate = (pre: number, post: number) => {
52
- if (preIndex > postIndex || (preIndex === -1) !== (postIndex === -1)) {
53
- throw new Error(`Error when trying to map ranges. Escaped string was: '${input}. [${[...input].join('|')}]'`);
54
- }
55
- };
56
- let preIndex = copy.indexOf(FuzzySearch.PRE);
57
- let postIndex = copy.indexOf(FuzzySearch.POST);
58
- validate(preIndex, postIndex);
59
- while (preIndex !== -1 && postIndex !== -1) {
60
- ranges.push({
61
- offset: preIndex,
62
- length: postIndex - preIndex - 1
63
- });
64
- copy.splice(postIndex, 1);
65
- copy.splice(preIndex, 1);
66
- preIndex = copy.indexOf(FuzzySearch.PRE);
67
- postIndex = copy.indexOf(FuzzySearch.POST);
68
- }
69
- if (ranges.length === 0) {
70
- throw new Error(`Unexpected zero ranges for match-string: ${input}.`);
71
- }
72
- return ranges;
73
- }
74
-
75
- }
76
-
77
- /**
78
- * Fuzzy searcher.
79
- */
80
- export namespace FuzzySearch {
81
-
82
- /**
83
- * A range representing the match region.
84
- */
85
- export interface Range {
86
-
87
- /**
88
- * The zero based offset of the match region.
89
- */
90
- readonly offset: number;
91
-
92
- /**
93
- * The length of the match region.
94
- */
95
- readonly length: number;
96
- }
97
-
98
- /**
99
- * A fuzzy search match.
100
- */
101
- export interface Match<T> {
102
-
103
- /**
104
- * The original item.
105
- */
106
- readonly item: T;
107
-
108
- /**
109
- * An array of ranges representing the match regions.
110
- */
111
- readonly ranges: ReadonlyArray<Range>;
112
- }
113
-
114
- /**
115
- * The fuzzy search input.
116
- */
117
- export interface Input<T> {
118
-
119
- /**
120
- * The pattern to match.
121
- */
122
- readonly pattern: string;
123
-
124
- /**
125
- * The items to filter based on the `pattern`.
126
- */
127
- readonly items: ReadonlyArray<T>;
128
-
129
- /**
130
- * Function that extracts the string from the inputs which will be used to evaluate the fuzzy matching filter.
131
- */
132
- readonly transform: (item: T) => string;
133
-
134
- }
135
-
136
- }
17
+ // Re-export from common location for backward compatibility.
18
+ export { FuzzySearch } from '../../common/fuzzy-search';
@@ -257,8 +257,7 @@ export class TreeViewWelcomeWidget extends TreeWidget {
257
257
  event.stopPropagation();
258
258
 
259
259
  if (value.startsWith('command:')) {
260
- const command = value.replace('command:', '');
261
- this.commands.executeCommand(command);
260
+ open(this.openerService, new URI(value));
262
261
  } else if (value.startsWith('file:')) {
263
262
  const uri = value.replace('file:', '');
264
263
  open(this.openerService, new URI(CodeUri.file(uri).toString()));
@@ -60,6 +60,7 @@ export class SelectComponent extends React.Component<SelectComponentProps, Selec
60
60
  protected mountedListeners: Map<string, EventListenerOrEventListenerObject> = new Map();
61
61
  protected optimalWidth = 0;
62
62
  protected optimalHeight = 0;
63
+ protected resizeObserver: ResizeObserver | undefined;
63
64
 
64
65
  constructor(props: SelectComponentProps) {
65
66
  super(props);
@@ -148,14 +149,16 @@ export class SelectComponent extends React.Component<SelectComponentProps, Selec
148
149
  return optimal + 20; // Just to be safe, add another 20 pixels here
149
150
  }
150
151
 
151
- protected attachListeners(): void {
152
- const hide = (event: MouseEvent) => {
152
+ protected attachListeners(): void {
153
+ const hide = (event: Event) => {
153
154
  if (!this.dropdownRef.current?.contains(event.target as Node)) {
154
155
  this.hide();
155
156
  }
156
157
  };
158
+ const hideOnResize = () => this.hide();
157
159
  this.mountedListeners.set('scroll', hide);
158
160
  this.mountedListeners.set('wheel', hide);
161
+ this.mountedListeners.set('resize', hideOnResize);
159
162
 
160
163
  let parent = this.fieldRef.current?.parentElement;
161
164
  while (parent) {
@@ -170,9 +173,43 @@ export class SelectComponent extends React.Component<SelectComponentProps, Selec
170
173
  for (const [key, listener] of this.mountedListeners.entries()) {
171
174
  window.addEventListener(key, listener);
172
175
  }
176
+
177
+ // Catch Lumino sash drags globally - observe the closest lm-Widget panel
178
+ const fieldEl = this.fieldRef.current;
179
+ const resizablePanel = fieldEl?.closest('.lm-Widget') ?? fieldEl?.parentElement;
180
+
181
+ if (resizablePanel && typeof ResizeObserver !== 'undefined') {
182
+ let lastWidth = 0;
183
+ let lastHeight = 0;
184
+ let isFirstFire = true;
185
+
186
+ this.resizeObserver = new ResizeObserver(entries => {
187
+ for (const entry of entries) {
188
+ const { width, height } = entry.contentRect;
189
+
190
+ // Ignore the initial automatic fire when the observer attaches
191
+ if (isFirstFire) {
192
+ lastWidth = width;
193
+ lastHeight = height;
194
+ isFirstFire = false;
195
+ continue;
196
+ }
197
+
198
+ // Only hide if the panel dimensions actually changed by more than 2 pixels
199
+ if (this.state.dimensions && (Math.abs(width - lastWidth) > 2 || Math.abs(height - lastHeight) > 2)) {
200
+ this.hide();
201
+ }
202
+
203
+ lastWidth = width;
204
+ lastHeight = height;
205
+ }
206
+ });
207
+ this.resizeObserver.observe(resizablePanel);
208
+ }
173
209
  }
174
210
 
175
211
  override componentWillUnmount(): void {
212
+ this.resizeObserver?.disconnect();
176
213
  if (this.mountedListeners.size > 0) {
177
214
  const eventListener = this.mountedListeners.get('scroll')!;
178
215
  let parent = this.fieldRef.current?.parentElement;
@@ -53,8 +53,13 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
53
53
  return undefined;
54
54
  }
55
55
 
56
- openNewDefaultWindow(): void {
56
+ async openNewDefaultWindow(): Promise<number> {
57
57
  this.openNewWindow(`#${DEFAULT_WINDOW_HASH}`);
58
+ return -1;
59
+ }
60
+
61
+ closeWindow(windowId: number): void {
62
+ // No-op in browser-only mode
58
63
  }
59
64
 
60
65
  focus(): void {
@@ -20,7 +20,8 @@ import { WindowService } from '../window-service';
20
20
  @injectable()
21
21
  export class MockWindowService implements WindowService {
22
22
  openNewWindow(): undefined { return undefined; }
23
- openNewDefaultWindow(): void { }
23
+ async openNewDefaultWindow(): Promise<number> { return -1; }
24
+ closeWindow(): void { }
24
25
  focus(): void { }
25
26
  reload(): void { }
26
27
  isSafeToShutDown(): Promise<boolean> { return Promise.resolve(true); }
@@ -39,8 +39,16 @@ export interface WindowService {
39
39
  /**
40
40
  * Opens a new default window.
41
41
  * - In electron and in the browser it will open the default window without a pre-defined content.
42
+ * @returns a window identifier that can be passed to {@link closeWindow}, or -1 if not supported.
42
43
  */
43
- openNewDefaultWindow(params?: WindowReloadOptions): void;
44
+ openNewDefaultWindow(params?: WindowReloadOptions): Promise<number>;
45
+
46
+ /**
47
+ * Closes a window previously opened by {@link openNewDefaultWindow}.
48
+ * @param windowId the identifier returned by {@link openNewDefaultWindow}.
49
+ * No-op if the window does not exist or the platform does not support it.
50
+ */
51
+ closeWindow(windowId: number): void;
44
52
 
45
53
  /**
46
54
  * Reveal and focuses the current window