@theia/core 1.75.0-next.3 → 1.75.0-next.34

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 (228) hide show
  1. package/README.md +15 -15
  2. package/lib/browser/authentication-service.js +1 -1
  3. package/lib/browser/authentication-service.js.map +1 -1
  4. package/lib/browser/browser-clipboard-service.js +1 -0
  5. package/lib/browser/browser-clipboard-service.js.map +1 -1
  6. package/lib/browser/catalog.json +130 -38
  7. package/lib/browser/common-frontend-contribution.d.ts +3 -0
  8. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  9. package/lib/browser/common-frontend-contribution.js +15 -3
  10. package/lib/browser/common-frontend-contribution.js.map +1 -1
  11. package/lib/browser/common-frontend-contribution.spec.d.ts +2 -0
  12. package/lib/browser/common-frontend-contribution.spec.d.ts.map +1 -0
  13. package/lib/browser/common-frontend-contribution.spec.js +71 -0
  14. package/lib/browser/common-frontend-contribution.spec.js.map +1 -0
  15. package/lib/browser/connection-status-service.d.ts +1 -0
  16. package/lib/browser/connection-status-service.d.ts.map +1 -1
  17. package/lib/browser/connection-status-service.js +7 -0
  18. package/lib/browser/connection-status-service.js.map +1 -1
  19. package/lib/browser/frontend-application-contribution.d.ts +1 -1
  20. package/lib/browser/frontend-application-state.js +1 -0
  21. package/lib/browser/frontend-application-state.js.map +1 -1
  22. package/lib/browser/icon-theme-service.d.ts +5 -3
  23. package/lib/browser/icon-theme-service.d.ts.map +1 -1
  24. package/lib/browser/icon-theme-service.js +7 -1
  25. package/lib/browser/icon-theme-service.js.map +1 -1
  26. package/lib/browser/keyboard/browser-keyboard-layout-provider.js +1 -0
  27. package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
  28. package/lib/browser/markdown-rendering/markdown-link-handler.d.ts +11 -0
  29. package/lib/browser/markdown-rendering/markdown-link-handler.d.ts.map +1 -1
  30. package/lib/browser/markdown-rendering/markdown-link-handler.js +23 -1
  31. package/lib/browser/markdown-rendering/markdown-link-handler.js.map +1 -1
  32. package/lib/browser/markdown-rendering/markdown-renderer.d.ts +11 -0
  33. package/lib/browser/markdown-rendering/markdown-renderer.d.ts.map +1 -1
  34. package/lib/browser/markdown-rendering/markdown-renderer.js +41 -0
  35. package/lib/browser/markdown-rendering/markdown-renderer.js.map +1 -1
  36. package/lib/browser/markdown-rendering/markdown-renderer.spec.d.ts +2 -0
  37. package/lib/browser/markdown-rendering/markdown-renderer.spec.d.ts.map +1 -0
  38. package/lib/browser/markdown-rendering/markdown-renderer.spec.js +79 -0
  39. package/lib/browser/markdown-rendering/markdown-renderer.spec.js.map +1 -0
  40. package/lib/browser/open-with-service.d.ts +2 -1
  41. package/lib/browser/open-with-service.d.ts.map +1 -1
  42. package/lib/browser/open-with-service.js +6 -1
  43. package/lib/browser/open-with-service.js.map +1 -1
  44. package/lib/browser/performance/frontend-stopwatch.d.ts +2 -1
  45. package/lib/browser/performance/frontend-stopwatch.d.ts.map +1 -1
  46. package/lib/browser/performance/frontend-stopwatch.js +6 -1
  47. package/lib/browser/performance/frontend-stopwatch.js.map +1 -1
  48. package/lib/browser/perspective-service.js +1 -1
  49. package/lib/browser/perspective-service.js.map +1 -1
  50. package/lib/browser/preferences/preference-proxy.spec.js +3 -0
  51. package/lib/browser/preferences/preference-proxy.spec.js.map +1 -1
  52. package/lib/browser/preferences/preference-service.spec.js +3 -0
  53. package/lib/browser/preferences/preference-service.spec.js.map +1 -1
  54. package/lib/browser/preferences/preference-validation-service.js +1 -1
  55. package/lib/browser/preferences/preference-validation-service.js.map +1 -1
  56. package/lib/browser/saveable-service.d.ts +2 -1
  57. package/lib/browser/saveable-service.d.ts.map +1 -1
  58. package/lib/browser/saveable-service.js +6 -1
  59. package/lib/browser/saveable-service.js.map +1 -1
  60. package/lib/browser/shell/application-shell.js +3 -3
  61. package/lib/browser/shell/application-shell.js.map +1 -1
  62. package/lib/browser/shell/application-shell.spec.d.ts +2 -0
  63. package/lib/browser/shell/application-shell.spec.d.ts.map +1 -0
  64. package/lib/browser/shell/application-shell.spec.js +57 -0
  65. package/lib/browser/shell/application-shell.spec.js.map +1 -0
  66. package/lib/browser/shell/shell-layout-restorer.d.ts +1 -1
  67. package/lib/browser/shell/shell-layout-restorer.d.ts.map +1 -1
  68. package/lib/browser/shell/shell-layout-restorer.js +5 -4
  69. package/lib/browser/shell/shell-layout-restorer.js.map +1 -1
  70. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -1
  71. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +3 -2
  72. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
  73. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +2 -1
  74. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -1
  75. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +6 -1
  76. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
  77. package/lib/browser/status-bar/status-bar-view-model.d.ts +2 -1
  78. package/lib/browser/status-bar/status-bar-view-model.d.ts.map +1 -1
  79. package/lib/browser/status-bar/status-bar-view-model.js +6 -1
  80. package/lib/browser/status-bar/status-bar-view-model.js.map +1 -1
  81. package/lib/browser/storage-service.d.ts +1 -1
  82. package/lib/browser/storage-service.d.ts.map +1 -1
  83. package/lib/browser/storage-service.js +1 -0
  84. package/lib/browser/storage-service.js.map +1 -1
  85. package/lib/browser/tree/tree-consistency.spec.js +103 -0
  86. package/lib/browser/tree/tree-consistency.spec.js.map +1 -1
  87. package/lib/browser/tree/tree-model.d.ts.map +1 -1
  88. package/lib/browser/tree/tree-model.js +1 -0
  89. package/lib/browser/tree/tree-model.js.map +1 -1
  90. package/lib/browser/tree/tree.d.ts.map +1 -1
  91. package/lib/browser/tree/tree.js +6 -0
  92. package/lib/browser/tree/tree.js.map +1 -1
  93. package/lib/browser/widget-manager.js +1 -0
  94. package/lib/browser/widget-manager.js.map +1 -1
  95. package/lib/browser/widgets/select-component.d.ts +13 -0
  96. package/lib/browser/widgets/select-component.d.ts.map +1 -1
  97. package/lib/browser/widgets/select-component.js +44 -12
  98. package/lib/browser/widgets/select-component.js.map +1 -1
  99. package/lib/browser/window/default-secondary-window-service.d.ts +4 -1
  100. package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
  101. package/lib/browser/window/default-secondary-window-service.js +17 -6
  102. package/lib/browser/window/default-secondary-window-service.js.map +1 -1
  103. package/lib/browser/window/default-window-service.d.ts +19 -0
  104. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  105. package/lib/browser/window/default-window-service.js +34 -5
  106. package/lib/browser/window/default-window-service.js.map +1 -1
  107. package/lib/browser/window/default-window-service.spec.js +22 -0
  108. package/lib/browser/window/default-window-service.spec.js.map +1 -1
  109. package/lib/browser/window/secondary-window-service.d.ts +7 -0
  110. package/lib/browser/window/secondary-window-service.d.ts.map +1 -1
  111. package/lib/browser/window/window-service.d.ts +1 -1
  112. package/lib/browser-only/frontend-only-application-module.d.ts.map +1 -1
  113. package/lib/browser-only/frontend-only-application-module.js +25 -1
  114. package/lib/browser-only/frontend-only-application-module.js.map +1 -1
  115. package/lib/common/command.d.ts +37 -0
  116. package/lib/common/command.d.ts.map +1 -1
  117. package/lib/common/command.js +74 -4
  118. package/lib/common/command.js.map +1 -1
  119. package/lib/common/command.spec.js +109 -0
  120. package/lib/common/command.spec.js.map +1 -1
  121. package/lib/common/message-service-protocol.d.ts +2 -0
  122. package/lib/common/message-service-protocol.d.ts.map +1 -1
  123. package/lib/common/message-service-protocol.js +8 -2
  124. package/lib/common/message-service-protocol.js.map +1 -1
  125. package/lib/common/performance/stopwatch.js +1 -0
  126. package/lib/common/performance/stopwatch.js.map +1 -1
  127. package/lib/common/preferences/preference-schema-service.d.ts +2 -0
  128. package/lib/common/preferences/preference-schema-service.d.ts.map +1 -1
  129. package/lib/common/preferences/preference-schema-service.js +11 -5
  130. package/lib/common/preferences/preference-schema-service.js.map +1 -1
  131. package/lib/common/preferences/preference-service.d.ts +2 -0
  132. package/lib/common/preferences/preference-service.d.ts.map +1 -1
  133. package/lib/common/preferences/preference-service.js +7 -1
  134. package/lib/common/preferences/preference-service.js.map +1 -1
  135. package/lib/electron-browser/menu/electron-menu-contribution.js +1 -1
  136. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  137. package/lib/electron-browser/window/electron-secondary-window-service.js +1 -1
  138. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  139. package/lib/electron-browser/window/electron-window-service.js +3 -3
  140. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  141. package/lib/node/backend-application.js +5 -5
  142. package/lib/node/backend-application.js.map +1 -1
  143. package/lib/node/backend-application.spec.js +6 -3
  144. package/lib/node/backend-application.spec.js.map +1 -1
  145. package/lib/node/env-variables/env-variables-server.d.ts +2 -0
  146. package/lib/node/env-variables/env-variables-server.d.ts.map +1 -1
  147. package/lib/node/env-variables/env-variables-server.js +7 -1
  148. package/lib/node/env-variables/env-variables-server.js.map +1 -1
  149. package/lib/node/hosting/browser-connection-token.d.ts +2 -0
  150. package/lib/node/hosting/browser-connection-token.d.ts.map +1 -1
  151. package/lib/node/hosting/browser-connection-token.js +14 -1
  152. package/lib/node/hosting/browser-connection-token.js.map +1 -1
  153. package/lib/node/hosting/browser-connection-token.spec.js +25 -2
  154. package/lib/node/hosting/browser-connection-token.spec.js.map +1 -1
  155. package/lib/node/key-store-server.d.ts +2 -0
  156. package/lib/node/key-store-server.d.ts.map +1 -1
  157. package/lib/node/key-store-server.js +6 -1
  158. package/lib/node/key-store-server.js.map +1 -1
  159. package/lib/node/messaging/default-messaging-service.js +1 -1
  160. package/lib/node/messaging/default-messaging-service.js.map +1 -1
  161. package/lib/node/messaging/ipc-connection-provider.js +1 -0
  162. package/lib/node/messaging/ipc-connection-provider.js.map +1 -1
  163. package/lib/node/messaging/websocket-endpoint.js +1 -1
  164. package/lib/node/messaging/websocket-endpoint.js.map +1 -1
  165. package/lib/node/messaging/websocket-frontend-connection-service.d.ts +2 -1
  166. package/lib/node/messaging/websocket-frontend-connection-service.d.ts.map +1 -1
  167. package/lib/node/messaging/websocket-frontend-connection-service.js +11 -6
  168. package/lib/node/messaging/websocket-frontend-connection-service.js.map +1 -1
  169. package/lib/node/setting-service.js +1 -0
  170. package/lib/node/setting-service.js.map +1 -1
  171. package/package.json +19 -19
  172. package/src/browser/authentication-service.ts +1 -1
  173. package/src/browser/browser-clipboard-service.ts +2 -2
  174. package/src/browser/common-frontend-contribution.spec.ts +84 -0
  175. package/src/browser/common-frontend-contribution.ts +15 -3
  176. package/src/browser/connection-status-service.ts +7 -3
  177. package/src/browser/frontend-application-contribution.ts +1 -1
  178. package/src/browser/frontend-application-state.ts +2 -2
  179. package/src/browser/icon-theme-service.ts +6 -2
  180. package/src/browser/keyboard/browser-keyboard-layout-provider.ts +2 -2
  181. package/src/browser/markdown-rendering/markdown-link-handler.ts +23 -1
  182. package/src/browser/markdown-rendering/markdown-renderer.spec.ts +88 -0
  183. package/src/browser/markdown-rendering/markdown-renderer.ts +43 -0
  184. package/src/browser/open-with-service.ts +6 -3
  185. package/src/browser/performance/frontend-stopwatch.ts +6 -3
  186. package/src/browser/perspective-service.ts +1 -1
  187. package/src/browser/preferences/preference-proxy.spec.ts +3 -0
  188. package/src/browser/preferences/preference-service.spec.ts +3 -0
  189. package/src/browser/preferences/preference-validation-service.ts +1 -1
  190. package/src/browser/saveable-service.ts +5 -2
  191. package/src/browser/shell/application-shell.spec.ts +62 -0
  192. package/src/browser/shell/application-shell.ts +4 -4
  193. package/src/browser/shell/shell-layout-restorer.ts +6 -5
  194. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +3 -2
  195. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +5 -2
  196. package/src/browser/status-bar/status-bar-view-model.ts +7 -3
  197. package/src/browser/storage-service.ts +4 -2
  198. package/src/browser/tree/tree-consistency.spec.ts +119 -0
  199. package/src/browser/tree/tree-model.ts +4 -2
  200. package/src/browser/tree/tree.ts +6 -0
  201. package/src/browser/widget-manager.ts +1 -1
  202. package/src/browser/widgets/select-component.tsx +49 -12
  203. package/src/browser/window/default-secondary-window-service.ts +17 -8
  204. package/src/browser/window/default-window-service.spec.ts +22 -0
  205. package/src/browser/window/default-window-service.ts +37 -5
  206. package/src/browser/window/secondary-window-service.ts +7 -0
  207. package/src/browser/window/window-service.ts +1 -1
  208. package/src/browser-only/frontend-only-application-module.ts +25 -1
  209. package/src/common/command.spec.ts +125 -0
  210. package/src/common/command.ts +77 -4
  211. package/src/common/message-service-protocol.ts +7 -3
  212. package/src/common/performance/stopwatch.ts +2 -2
  213. package/src/common/preferences/preference-schema-service.ts +9 -5
  214. package/src/common/preferences/preference-service.ts +6 -2
  215. package/src/electron-browser/menu/electron-menu-contribution.ts +1 -1
  216. package/src/electron-browser/window/electron-secondary-window-service.ts +1 -1
  217. package/src/electron-browser/window/electron-window-service.ts +3 -3
  218. package/src/node/backend-application.spec.ts +6 -3
  219. package/src/node/backend-application.ts +5 -5
  220. package/src/node/env-variables/env-variables-server.ts +6 -2
  221. package/src/node/hosting/browser-connection-token.spec.ts +30 -3
  222. package/src/node/hosting/browser-connection-token.ts +13 -2
  223. package/src/node/key-store-server.ts +6 -3
  224. package/src/node/messaging/default-messaging-service.ts +1 -1
  225. package/src/node/messaging/ipc-connection-provider.ts +2 -2
  226. package/src/node/messaging/websocket-endpoint.ts +1 -1
  227. package/src/node/messaging/websocket-frontend-connection-service.ts +11 -8
  228. package/src/node/setting-service.ts +2 -2
@@ -101,5 +101,48 @@ export class MarkdownRendererImpl implements MarkdownRenderer {
101
101
  return `<i class="${codicon(chunk.name)} ${chunk.animation ? `fa-${chunk.animation}` : ''} icon-inline"></i>`;
102
102
  }).join('');
103
103
  };
104
+
105
+ // Support VS Code's `![alt](src|width=W,height=H)` image sizing syntax, which markdown-it
106
+ // would otherwise fold into the `src` attribute, yielding a broken image.
107
+ const renderImage = this.markdownIt.renderer.rules.image;
108
+ this.markdownIt.renderer.rules.image = (tokens, idx, options, env, self) => {
109
+ const token = tokens[idx];
110
+ const srcIndex = token.attrIndex('src');
111
+ if (srcIndex >= 0 && token.attrs) {
112
+ const src = token.attrs[srcIndex][1];
113
+ // markdown-it percent-encodes the link destination, so the separator may have
114
+ // become `%7C` by the time the token reaches the renderer.
115
+ const separator = /(?:\||%7c)(?![\s\S]*(?:\||%7c))/i.exec(src);
116
+ if (separator) {
117
+ const dimensions = this.parseImageDimensions(src.substring(separator.index + separator[0].length));
118
+ if (dimensions) {
119
+ token.attrs[srcIndex][1] = src.substring(0, separator.index);
120
+ for (const [name, value] of Object.entries(dimensions)) {
121
+ token.attrSet(name, value);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ return renderImage ? renderImage(tokens, idx, options, env, self) : self.renderToken(tokens, idx, options);
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Parses the `width=W,height=H` parameter list of an image's sizing suffix.
132
+ *
133
+ * @returns the recognized dimensions, or `undefined` if the suffix is not a well-formed
134
+ * parameter list — in which case it is left as part of the `src`, since it may be a literal
135
+ * `|` in the URL.
136
+ */
137
+ protected parseImageDimensions(params: string): { width?: string, height?: string } | undefined {
138
+ const dimensions: { width?: string, height?: string } = {};
139
+ for (const param of params.split(',')) {
140
+ const match = /^\s*(width|height)\s*=\s*(\d+)\s*$/.exec(param);
141
+ if (!match) {
142
+ return undefined;
143
+ }
144
+ dimensions[match[1] as 'width' | 'height'] = match[2];
145
+ }
146
+ return Object.keys(dimensions).length > 0 ? dimensions : undefined;
104
147
  }
105
148
  }
@@ -14,14 +14,14 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { inject, injectable } from 'inversify';
17
+ import { inject, injectable, named } from 'inversify';
18
18
  import { Disposable } from '../common/disposable';
19
19
  import { nls } from '../common/nls';
20
20
  import { MaybePromise } from '../common/types';
21
21
  import { URI } from '../common/uri';
22
22
  import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator } from './quick-input';
23
23
  import { getDefaultHandler } from './opener-service';
24
- import { PreferenceService, PreferenceScope } from '../common';
24
+ import { PreferenceService, PreferenceScope, ILogger } from '../common';
25
25
 
26
26
  export interface OpenWithHandler {
27
27
  /**
@@ -74,11 +74,14 @@ export class OpenWithService {
74
74
  @inject(PreferenceService)
75
75
  protected readonly preferenceService: PreferenceService;
76
76
 
77
+ @inject(ILogger) @named('core:OpenWithService')
78
+ protected readonly logger: ILogger;
79
+
77
80
  protected readonly handlers: OpenWithHandler[] = [];
78
81
 
79
82
  registerHandler(handler: OpenWithHandler): Disposable {
80
83
  if (this.handlers.some(h => h.id === handler.id)) {
81
- console.warn('Duplicate OpenWithHandler registration: ' + handler.id);
84
+ this.logger.warn('Duplicate OpenWithHandler registration: ' + handler.id);
82
85
  return Disposable.NULL;
83
86
  }
84
87
  this.handlers.push(handler);
@@ -14,12 +14,15 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  *******************************************************************************/
16
16
 
17
- import { injectable } from 'inversify';
18
- import { Measurement, MeasurementOptions, Stopwatch } from '../../common';
17
+ import { injectable, inject, named } from 'inversify';
18
+ import { Measurement, MeasurementOptions, Stopwatch, ILogger } from '../../common';
19
19
 
20
20
  @injectable()
21
21
  export class FrontendStopwatch extends Stopwatch {
22
22
 
23
+ @inject(ILogger) @named('core:FrontendStopwatch')
24
+ protected override readonly logger: ILogger;
25
+
23
26
  constructor() {
24
27
  super({
25
28
  owner: 'frontend page',
@@ -51,7 +54,7 @@ export class FrontendStopwatch extends Stopwatch {
51
54
  duration = entries[0].duration ?? Number.NaN;
52
55
  startTime = entries[0].startTime ?? Number.NaN;
53
56
  } catch (e) {
54
- console.warn(e);
57
+ this.logger.warn(e);
55
58
  duration = Number.NaN;
56
59
  startTime = Number.NaN;
57
60
  }
@@ -196,7 +196,7 @@ export class PerspectiveServiceImpl implements FrontendApplicationContribution,
196
196
  @inject(QuickInputService) @optional()
197
197
  protected readonly quickInputService: QuickInputService | undefined;
198
198
 
199
- @inject(ILogger) @named('core:PerspectiveService')
199
+ @inject(ILogger) @named('core:PerspectiveServiceImpl')
200
200
  protected readonly logger: ILogger;
201
201
 
202
202
  @inject(WidgetAreaResolver)
@@ -28,6 +28,8 @@ import { PreferenceScope } from '../../common/preferences/preference-scope';
28
28
  import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider';
29
29
  import { PreferenceProxyOptions, PreferenceProxy, PreferenceChangeEvent, createPreferenceProxy } from '../../common/preferences/preference-proxy';
30
30
  import { PreferenceProxyFactory } from '../../common/preferences/injectable-preference-proxy';
31
+ import { ILogger } from '../../common/logger';
32
+ import { MockLogger } from '../../common/test/mock-logger';
31
33
 
32
34
  disableJSDOM();
33
35
 
@@ -47,6 +49,7 @@ function createTestContainer(): Container {
47
49
  const result = new Container();
48
50
  bindPreferenceService(result.bind.bind(result));
49
51
  bindMockPreferenceProviders(result.bind.bind(result), result.unbind.bind(result));
52
+ result.bind(ILogger).to(MockLogger);
50
53
  return result;
51
54
  }
52
55
 
@@ -28,6 +28,8 @@ import { FrontendApplicationConfigProvider } from '../frontend-application-confi
28
28
  import { bindMockPreferenceProviders, MockPreferenceProvider } from './test';
29
29
  import { PreferenceChangeEvent, createPreferenceProxy } from '../../common/preferences/preference-proxy';
30
30
  import { bindPreferenceService } from '../frontend-application-bindings';
31
+ import { ILogger } from '../../common/logger';
32
+ import { MockLogger } from '../../common/test/mock-logger';
31
33
 
32
34
  disableJSDOM();
33
35
 
@@ -43,6 +45,7 @@ function createTestContainer(): Container {
43
45
  const result = new Container();
44
46
  bindPreferenceService(result.bind.bind(result));
45
47
  bindMockPreferenceProviders(result.bind.bind(result), result.unbind.bind(result));
48
+ result.bind(ILogger).to(MockLogger);
46
49
  return result;
47
50
  }
48
51
 
@@ -175,7 +175,7 @@ export class PreferenceValidationService {
175
175
  const configuredDefault = this.getDefaultFromSchema(schema);
176
176
  return this.validateOneOf(key, configuredDefault, { ...schema, default: undefined });
177
177
  }
178
- console.log(`While validating ${key}, failed to find a valid value or default value. Using configured value ${value}.`);
178
+ this.logger.info(`While validating ${key}, failed to find a valid value or default value. Using configured value ${value}.`);
179
179
  return value;
180
180
  }
181
181
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  import type { ApplicationShell } from './shell';
18
18
  import { inject, injectable, named } from 'inversify';
19
- import { ContributionProvider, UNTITLED_SCHEME, URI, Disposable, DisposableCollection, Emitter, Event } from '../common';
19
+ import { ContributionProvider, UNTITLED_SCHEME, URI, Disposable, DisposableCollection, Emitter, Event, ILogger } 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';
@@ -53,6 +53,9 @@ export class SaveableService implements FrontendApplicationContribution {
53
53
  @inject(WindowFocusService)
54
54
  protected readonly windowFocusService: WindowFocusService;
55
55
 
56
+ @inject(ILogger) @named('core:SaveableService')
57
+ protected readonly logger: ILogger;
58
+
56
59
  protected saveThrottles = new Map<Widget, AutoSaveThrottle>();
57
60
  protected saveMode: AutoSaveMode = 'off';
58
61
  protected saveDelay = 1000;
@@ -284,7 +287,7 @@ export class SaveableService implements FrontendApplicationContribution {
284
287
  }
285
288
  const saveable = Saveable.get(widget);
286
289
  if (!saveable) {
287
- console.warn('Saveable.get returned undefined on a known saveable widget. This is unexpected.');
290
+ this.logger.warn('Saveable.get returned undefined on a known saveable widget. This is unexpected.');
288
291
  }
289
292
  // Enter branch if saveable absent since we cannot check autosaveability more definitely.
290
293
  if (this.autoSave !== 'off' && (!saveable || this.shouldAutoSave(widget, saveable))) {
@@ -0,0 +1,62 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Alec Timison.
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 { enableJSDOM } from '../test/jsdom';
18
+ const disableJSDOM = enableJSDOM();
19
+
20
+ import { expect } from 'chai';
21
+ import { Widget } from '@theia/core/shared/@lumino/widgets';
22
+ import * as sinon from 'sinon';
23
+ import { DisposableCollection } from '../../common/disposable';
24
+ import { ApplicationShell } from './application-shell';
25
+ disableJSDOM();
26
+
27
+ describe('ApplicationShell', () => {
28
+ let toTearDown: () => void;
29
+
30
+ beforeEach(() => {
31
+ toTearDown = enableJSDOM();
32
+ });
33
+
34
+ afterEach(() => {
35
+ sinon.restore();
36
+ toTearDown();
37
+ });
38
+
39
+ it('cancels the previous activation check', () => {
40
+ const shell = Object.create(ApplicationShell.prototype) as ApplicationShell;
41
+ Object.assign(shell, {
42
+ toDisposeOnActivationCheck: new DisposableCollection(),
43
+ logger: { warn: sinon.stub() }
44
+ });
45
+ const assertActivated = (shell as unknown as { assertActivated(widget: Widget): void }).assertActivated.bind(shell);
46
+ const firstWidget = new Widget();
47
+ const secondWidget = new Widget();
48
+ const request = {} as ReturnType<typeof window.setTimeout>;
49
+ sinon.stub(globalThis, 'setTimeout').returns(request);
50
+ sinon.stub(window, 'setTimeout').returns(request);
51
+ const clearTimeout = sinon.stub(window, 'clearTimeout');
52
+ window.cancelAnimationFrame = sinon.stub();
53
+
54
+ assertActivated(firstWidget);
55
+ assertActivated(secondWidget);
56
+
57
+ expect(clearTimeout.callCount).to.equal(1);
58
+ expect(clearTimeout.firstCall.args[0]).to.equal(request);
59
+ firstWidget.dispose();
60
+ secondWidget.dispose();
61
+ });
62
+ });
@@ -1438,7 +1438,7 @@ export class ApplicationShell extends Widget {
1438
1438
  this.toDisposeOnActivationCheck.push(Disposable.create(() => widget.disposed.disconnect(onDispose)));
1439
1439
 
1440
1440
  let start = 0;
1441
- const step: FrameRequestCallback = () => {
1441
+ const step = (): void => {
1442
1442
  const activeElement = widget.node.ownerDocument.activeElement;
1443
1443
  if (activeElement && widget.node.contains(activeElement)) {
1444
1444
  return;
@@ -1449,13 +1449,13 @@ export class ApplicationShell extends Widget {
1449
1449
  }
1450
1450
  const delta = now - start;
1451
1451
  if (delta < this.activationTimeout) {
1452
- request = setTimeout(step, 0);
1452
+ request = window.setTimeout(step, 0);
1453
1453
  } else {
1454
1454
  this.logger.warn(`Widget was activated, but did not accept focus after ${this.activationTimeout}ms: ${widget.id}`);
1455
1455
  }
1456
1456
  };
1457
- let request = setTimeout(step, 0);
1458
- this.toDisposeOnActivationCheck.push(Disposable.create(() => window.cancelAnimationFrame(request)));
1457
+ let request = window.setTimeout(step, 0);
1458
+ this.toDisposeOnActivationCheck.push(Disposable.create(() => window.clearTimeout(request)));
1459
1459
  }
1460
1460
 
1461
1461
  /**
@@ -154,7 +154,8 @@ export class ShellLayoutRestorer implements CommandContribution {
154
154
 
155
155
  constructor(
156
156
  @inject(WidgetManager) protected widgetManager: WidgetManager,
157
- @inject(ILogger) protected logger: ILogger,
157
+ @inject(ILogger) @named('core:ShellLayoutRestorer')
158
+ protected readonly logger: ILogger,
158
159
  @inject(StorageService) protected storageService: StorageService) { }
159
160
 
160
161
  registerCommands(commands: CommandRegistry): void {
@@ -374,18 +375,18 @@ export class ShellLayoutRestorer implements CommandContribution {
374
375
  }
375
376
  if (layoutVersion !== applicationShellLayoutVersion) {
376
377
  if (layoutVersion < applicationShellLayoutVersion) {
377
- console.warn(`Layout version ${layoutVersion} is behind current layout version ${applicationShellLayoutVersion}, trying to migrate...`);
378
+ this.logger.warn(`Layout version ${layoutVersion} is behind current layout version ${applicationShellLayoutVersion}, trying to migrate...`);
378
379
  } else {
379
- console.warn(`Layout version ${layoutVersion} is ahead current layout version ${applicationShellLayoutVersion}, trying to load anyway...`);
380
+ this.logger.warn(`Layout version ${layoutVersion} is ahead current layout version ${applicationShellLayoutVersion}, trying to load anyway...`);
380
381
  }
381
- console.info(`Please use '${RESET_LAYOUT.label}' command if the layout looks bogus.`);
382
+ this.logger.info(`Please use '${RESET_LAYOUT.label}' command if the layout looks bogus.`);
382
383
  }
383
384
 
384
385
  const migrations = this.migrations.getContributions()
385
386
  .filter(m => m.layoutVersion > layoutVersion && m.layoutVersion <= applicationShellLayoutVersion)
386
387
  .sort((m, m2) => m.layoutVersion - m2.layoutVersion);
387
388
  if (migrations.length) {
388
- console.info(`Found ${migrations.length} migrations from layout version ${layoutVersion} to version ${applicationShellLayoutVersion}, migrating...`);
389
+ this.logger.info(`Found ${migrations.length} migrations from layout version ${layoutVersion} to version ${applicationShellLayoutVersion}, migrating...`);
389
390
  }
390
391
 
391
392
  const context = { layout, layoutVersion, migrations };
@@ -96,8 +96,9 @@ abstract class AbstractToolbarMenuWrapper {
96
96
  const icon = this.icon || 'ellipsis';
97
97
  const contextMatcher: ContextMatcher = this.contextKeyService;
98
98
  const className = `${icon} ${ACTION_ITEM}`;
99
+ const itemClassName = TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu' + (this.isToggled(widget) ? ' toggled' : '');
99
100
  if (CompoundMenuNode.is(this.menuNode) && !this.menuNode.isEmpty(this.effectiveMenuPath, this.contextKeyService, widget.node, widget)) {
100
- return <div key={this.id} className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu'}>
101
+ return <div key={this.id} className={itemClassName}>
101
102
  <div id={this.id} className={className}
102
103
  title={this.tooltip || this.text}
103
104
  onClick={e => this.executeCommand(widget, e)}
@@ -107,7 +108,7 @@ abstract class AbstractToolbarMenuWrapper {
107
108
  </div>
108
109
  </div>;
109
110
  } else {
110
- return <div key={this.id} className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu'}>
111
+ return <div key={this.id} className={itemClassName}>
111
112
  <div id={this.id} className={className}
112
113
  title={this.tooltip || this.text}
113
114
  onClick={e => this.executeCommand(widget, e)}
@@ -17,7 +17,7 @@
17
17
  import debounce = require('lodash.debounce');
18
18
  import { inject, injectable, named } from 'inversify';
19
19
  // eslint-disable-next-line max-len
20
- import { CommandRegistry, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath } from '../../../common';
20
+ import { CommandRegistry, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath, ILogger } from '../../../common';
21
21
  import { ContextKeyService } from '../../context-key-service';
22
22
  import { FrontendApplicationContribution } from '../../frontend-application-contribution';
23
23
  import { Widget } from '../../widgets';
@@ -68,6 +68,9 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
68
68
  @inject(ContributionProvider) @named(TabBarToolbarContribution)
69
69
  protected readonly contributionProvider: ContributionProvider<TabBarToolbarContribution>;
70
70
 
71
+ @inject(ILogger) @named('core:TabBarToolbarRegistry')
72
+ protected readonly logger: ILogger;
73
+
71
74
  protected readonly onDidChangeEmitter = new Emitter<void>();
72
75
  readonly onDidChange: Event<void> = this.onDidChangeEmitter.event;
73
76
  // debounce in order to avoid to fire more than once in the same tick
@@ -223,7 +226,7 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
223
226
  this.fireOnDidChange();
224
227
  return { dispose: () => this.unregisterMenuDelegate(menuPath) };
225
228
  }
226
- console.warn('Unable to register menu delegate. Delegate has already been registered', menuPath);
229
+ this.logger.warn('Unable to register menu delegate. Delegate has already been registered', menuPath);
227
230
  return Disposable.NULL;
228
231
  }
229
232
 
@@ -14,8 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { injectable } from 'inversify';
18
- import { ArrayUtils, Emitter, Event } from '../../common';
17
+ import { injectable, inject, named } from 'inversify';
18
+ import { ArrayUtils, Emitter, Event, ILogger } from '../../common';
19
19
  import { StatusBarAlignment, StatusBarEntry, StatusBarViewEntry, StatusBarViewModelEntry } from './status-bar-types';
20
20
 
21
21
  interface EntryLocation {
@@ -30,6 +30,10 @@ export class StatusBarViewModel {
30
30
  protected rightTree = new Array<StatusBarViewModelEntry>();
31
31
  protected containerPointers = new Map<string, StatusBarViewModelEntry[]>();
32
32
  protected onDidChangeEmitter = new Emitter<void>();
33
+
34
+ @inject(ILogger) @named('core:StatusBarViewModel')
35
+ protected readonly logger: ILogger;
36
+
33
37
  get onDidChange(): Event<void> {
34
38
  return this.onDidChangeEmitter.event;
35
39
  }
@@ -53,7 +57,7 @@ export class StatusBarViewModel {
53
57
  *getChildren(list: StatusBarViewModelEntry[], alignment: StatusBarAlignment, compact?: boolean): IterableIterator<StatusBarViewEntry> {
54
58
  for (const item of list) {
55
59
  if (item.leftChildren.length || item.rightChildren.length) {
56
- console.warn(`Found embedded entries with affinity to ${item.id}. They will inherit alignment and compactness of parent.`);
60
+ this.logger.warn(`Found embedded entries with affinity to ${item.id}. They will inherit alignment and compactness of parent.`);
57
61
  }
58
62
  yield* this.getChildren(item.leftChildren, alignment, item.head.affinity?.compact);
59
63
  yield { entry: item.head, id: item.id, alignment, compact: compact || item.head.affinity?.compact };
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { inject, injectable, postConstruct } from 'inversify';
17
+ import { inject, injectable, postConstruct, named } from 'inversify';
18
18
  import { ILogger } from '../common/logger';
19
19
  import { MessageService } from '../common/message-service';
20
20
  import { WindowService } from './window/window-service';
@@ -47,7 +47,9 @@ interface LocalStorage {
47
47
  export class LocalStorageService implements StorageService {
48
48
  private storage: LocalStorage;
49
49
 
50
- @inject(ILogger) protected logger: ILogger;
50
+ @inject(ILogger) @named('core:LocalStorageService')
51
+ protected readonly logger: ILogger;
52
+
51
53
  @inject(MessageService) protected readonly messageService: MessageService;
52
54
  @inject(WindowService) protected readonly windowService: WindowService;
53
55
 
@@ -21,6 +21,10 @@ import { TreeImpl, CompositeTreeNode, TreeNode } from './tree';
21
21
  import { TreeModel } from './tree-model';
22
22
  import { ExpandableTreeNode } from './tree-expansion';
23
23
  import { TreeLabelProvider } from './tree-label-provider';
24
+ import { MockTreeModel } from './test/mock-tree-model';
25
+ import { MockLogger } from '../../common/test/mock-logger';
26
+ import { ILogger } from '../../common';
27
+ import { Deferred, timeout } from '../../common/promise-util';
24
28
 
25
29
  @injectable()
26
30
  class ConsistencyTestTree extends TreeImpl {
@@ -69,6 +73,48 @@ function createConsistencyTestRoot(rootName: string): CompositeTreeNode {
69
73
  return root;
70
74
  }
71
75
 
76
+ /**
77
+ * A `TreeImpl` whose `resolveChildren` can be stalled or overridden per node id, to
78
+ * simulate slow child resolution (e.g. a file system request) in tests.
79
+ */
80
+ @injectable()
81
+ class StallingTestTree extends TreeImpl {
82
+
83
+ protected readonly stalled = new Map<string, Deferred<TreeNode[]>>();
84
+ protected readonly overrides = new Map<string, () => TreeNode[]>();
85
+
86
+ /**
87
+ * Stall the *next* `resolveChildren` for `id` on the returned deferred. Later
88
+ * resolutions for the same id proceed normally, so the stall cannot leak into
89
+ * unrelated refreshes (e.g. the cascade of a newly set root).
90
+ */
91
+ stallNextResolve(id: string): Deferred<TreeNode[]> {
92
+ const gate = new Deferred<TreeNode[]>();
93
+ this.stalled.set(id, gate);
94
+ return gate;
95
+ }
96
+
97
+ /** Make the *next* `resolveChildren` for `id` return `children()` instead of the current child nodes. */
98
+ overrideNextResolve(id: string, children: () => TreeNode[]): void {
99
+ this.overrides.set(id, children);
100
+ }
101
+
102
+ protected override resolveChildren(parent: CompositeTreeNode): Promise<TreeNode[]> {
103
+ const gate = this.stalled.get(parent.id);
104
+ if (gate) {
105
+ this.stalled.delete(parent.id);
106
+ return gate.promise;
107
+ }
108
+ const override = this.overrides.get(parent.id);
109
+ if (override) {
110
+ this.overrides.delete(parent.id);
111
+ return Promise.resolve(override());
112
+ }
113
+ return super.resolveChildren(parent);
114
+ }
115
+
116
+ }
117
+
72
118
  describe('Tree Consistency', () => {
73
119
 
74
120
  it('setting different tree roots should finish', async () => {
@@ -102,4 +148,77 @@ describe('Tree Consistency', () => {
102
148
  assert.ok(false, 'Resolving does not stop, attempts: ' + tree.resolveCounter);
103
149
  });
104
150
 
151
+ describe('stale refreshes', () => {
152
+
153
+ let tree: StallingTestTree;
154
+ let model: TreeModel;
155
+ let loggedErrors: unknown[][];
156
+
157
+ beforeEach(async () => {
158
+ const container = createTreeTestContainer();
159
+ container.bind(StallingTestTree).toSelf();
160
+ container.rebind(TreeImpl).toService(StallingTestTree);
161
+ tree = container.get(StallingTestTree);
162
+ model = container.get<TreeModel>(TreeModel);
163
+ loggedErrors = [];
164
+ const logger = container.get<MockLogger>(ILogger);
165
+ logger.error = async (...args: unknown[]) => { loggedErrors.push(args); };
166
+ model.root = MockTreeModel.HIERARCHICAL_MOCK_ROOT();
167
+ // let the refresh cascade triggered by setting the root settle
168
+ await timeout(0);
169
+ });
170
+
171
+ it('drops an in-flight refresh when the root is replaced meanwhile', async () => {
172
+ const stale = model.getNode('1.2') as CompositeTreeNode;
173
+ const gate = tree.stallNextResolve('1.2');
174
+ const pendingRefresh = tree.refresh(stale);
175
+
176
+ model.root = MockTreeModel.HIERARCHICAL_MOCK_ROOT();
177
+ gate.resolve(Array.from(stale.children));
178
+
179
+ assert.strictEqual(await pendingRefresh, undefined);
180
+ assert.deepStrictEqual(loggedErrors, []);
181
+ const fresh = model.getNode('1.2');
182
+ assert.ok(fresh);
183
+ assert.notStrictEqual(fresh, stale);
184
+ });
185
+
186
+ it('does not resurrect a removed subtree when a stale refresh completes', async () => {
187
+ const target = model.getNode('1.2') as CompositeTreeNode;
188
+ const gate = tree.stallNextResolve('1.2');
189
+ const pendingRefresh = tree.refresh(target);
190
+
191
+ CompositeTreeNode.removeChild(target.parent as CompositeTreeNode, target, tree);
192
+ assert.strictEqual(model.getNode('1.2'), undefined);
193
+ gate.resolve(Array.from(target.children));
194
+
195
+ assert.strictEqual(await pendingRefresh, undefined);
196
+ assert.strictEqual(model.getNode('1.2'), undefined);
197
+ assert.strictEqual(model.getNode('1.2.1'), undefined);
198
+ });
199
+
200
+ it('does not re-index a node object that a concurrent ancestor refresh replaced', async () => {
201
+ const stale = model.getNode('1.2') as CompositeTreeNode;
202
+ const gate = tree.stallNextResolve('1.2');
203
+ const pendingRefresh = tree.refresh(stale);
204
+
205
+ // an ancestor refresh yields a *fresh* object for the same id, as trees whose
206
+ // `resolveChildren` builds new nodes do
207
+ const parent = model.getNode('1') as CompositeTreeNode;
208
+ const replacement: CompositeTreeNode = { id: '1.2', name: '1.2', parent, children: [] };
209
+ tree.overrideNextResolve('1', () => parent.children.map(child => child.id === '1.2' ? replacement : child));
210
+ await tree.refresh(parent);
211
+ assert.strictEqual(model.getNode('1.2'), replacement);
212
+
213
+ gate.resolve(Array.from(stale.children));
214
+
215
+ assert.strictEqual(await pendingRefresh, undefined);
216
+ assert.deepStrictEqual(loggedErrors, []);
217
+ // the stale object must not take the replacement's place in the index, nor bring its children back
218
+ assert.strictEqual(model.getNode('1.2'), replacement);
219
+ assert.strictEqual(model.getNode('1.2.1'), undefined);
220
+ });
221
+
222
+ });
223
+
105
224
  });
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { inject, injectable, postConstruct } from 'inversify';
17
+ import { inject, injectable, postConstruct, named } from 'inversify';
18
18
  import { Event, Emitter, WaitUntilEvent } from '../../common/event';
19
19
  import { DisposableCollection } from '../../common/disposable';
20
20
  import { CancellationToken } from '../../common/cancellation';
@@ -165,7 +165,9 @@ export interface TreeModel extends Tree, TreeSelectionService, TreeExpansionServ
165
165
  @injectable()
166
166
  export class TreeModelImpl implements TreeModel, SelectionProvider<ReadonlyArray<Readonly<SelectableTreeNode>>> {
167
167
 
168
- @inject(ILogger) protected readonly logger: ILogger;
168
+ @inject(ILogger) @named('core:TreeModelImpl')
169
+ protected readonly logger: ILogger;
170
+
169
171
  @inject(Tree) protected readonly tree: Tree;
170
172
  @inject(TreeSelectionService) protected readonly selectionService: TreeSelectionService;
171
173
  @inject(TreeExpansionService) protected readonly expansionService: TreeExpansionService;
@@ -354,6 +354,12 @@ export class TreeImpl implements Tree {
354
354
  result = parent;
355
355
  const children = await this.resolveChildren(parent);
356
356
  if (cancellationToken?.isCancellationRequested) { return; }
357
+ if (this.validateNode(parent) !== parent) {
358
+ // the tree changed while children were being resolved (e.g. a new root was
359
+ // set or the node was removed): drop the stale refresh
360
+ this.logger.debug(`Refresh of node '${parent.id}' dropped, it no longer belongs to the tree.`);
361
+ return undefined;
362
+ }
357
363
  result = await this.setChildren(parent, children);
358
364
  if (cancellationToken?.isCancellationRequested) { return; }
359
365
  } finally {
@@ -119,7 +119,7 @@ export class WidgetManager {
119
119
  @inject(ContributionProvider) @named(WidgetFactory)
120
120
  protected readonly factoryProvider: ContributionProvider<WidgetFactory>;
121
121
 
122
- @inject(ILogger)
122
+ @inject(ILogger) @named('core:WidgetManager')
123
123
  protected readonly logger: ILogger;
124
124
 
125
125
  protected readonly onWillCreateWidgetEmitter = new Emitter<WillCreateWidgetEvent>();