@theia/core 1.64.0-next.17 → 1.64.0-next.35

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 (87) hide show
  1. package/README.md +1 -1
  2. package/lib/browser/catalog.json +161 -16
  3. package/lib/browser/context-key-service.d.ts +4 -0
  4. package/lib/browser/context-key-service.d.ts.map +1 -1
  5. package/lib/browser/context-key-service.js.map +1 -1
  6. package/lib/browser/menu/browser-menu-plugin.js +2 -2
  7. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  8. package/lib/browser/saveable-service.d.ts.map +1 -1
  9. package/lib/browser/saveable-service.js +4 -0
  10. package/lib/browser/saveable-service.js.map +1 -1
  11. package/lib/browser/tree/tree-widget.d.ts +6 -0
  12. package/lib/browser/tree/tree-widget.d.ts.map +1 -1
  13. package/lib/browser/tree/tree-widget.js +30 -23
  14. package/lib/browser/tree/tree-widget.js.map +1 -1
  15. package/lib/common/array-utils.d.ts +4 -0
  16. package/lib/common/array-utils.d.ts.map +1 -1
  17. package/lib/common/array-utils.js +47 -0
  18. package/lib/common/array-utils.js.map +1 -1
  19. package/lib/common/diff.d.ts +33 -0
  20. package/lib/common/diff.d.ts.map +1 -0
  21. package/lib/common/diff.js +20 -0
  22. package/lib/common/diff.js.map +1 -0
  23. package/lib/common/message-rpc/msg-pack-extension-manager.js +2 -2
  24. package/lib/common/message-rpc/msg-pack-extension-manager.js.map +1 -1
  25. package/lib/common/observable/autorun.d.ts +81 -0
  26. package/lib/common/observable/autorun.d.ts.map +1 -0
  27. package/lib/common/observable/autorun.js +194 -0
  28. package/lib/common/observable/autorun.js.map +1 -0
  29. package/lib/common/observable/derived-observable.d.ts +71 -0
  30. package/lib/common/observable/derived-observable.d.ts.map +1 -0
  31. package/lib/common/observable/derived-observable.js +258 -0
  32. package/lib/common/observable/derived-observable.js.map +1 -0
  33. package/lib/common/observable/index.d.ts +8 -0
  34. package/lib/common/observable/index.d.ts.map +1 -0
  35. package/lib/common/observable/index.js +26 -0
  36. package/lib/common/observable/index.js.map +1 -0
  37. package/lib/common/observable/observable-base.d.ts +181 -0
  38. package/lib/common/observable/observable-base.d.ts.map +1 -0
  39. package/lib/common/observable/observable-base.js +183 -0
  40. package/lib/common/observable/observable-base.js.map +1 -0
  41. package/lib/common/observable/observable-from-event.d.ts +41 -0
  42. package/lib/common/observable/observable-from-event.d.ts.map +1 -0
  43. package/lib/common/observable/observable-from-event.js +115 -0
  44. package/lib/common/observable/observable-from-event.js.map +1 -0
  45. package/lib/common/observable/observable-signal.d.ts +9 -0
  46. package/lib/common/observable/observable-signal.d.ts.map +1 -0
  47. package/lib/common/observable/observable-signal.js +45 -0
  48. package/lib/common/observable/observable-signal.js.map +1 -0
  49. package/lib/common/observable/observable-utils.d.ts +26 -0
  50. package/lib/common/observable/observable-utils.d.ts.map +1 -0
  51. package/lib/common/observable/observable-utils.js +98 -0
  52. package/lib/common/observable/observable-utils.js.map +1 -0
  53. package/lib/common/observable/observable.spec.d.ts +2 -0
  54. package/lib/common/observable/observable.spec.d.ts.map +1 -0
  55. package/lib/common/observable/observable.spec.js +301 -0
  56. package/lib/common/observable/observable.spec.js.map +1 -0
  57. package/lib/common/observable/settable-observable.d.ts +16 -0
  58. package/lib/common/observable/settable-observable.d.ts.map +1 -0
  59. package/lib/common/observable/settable-observable.js +58 -0
  60. package/lib/common/observable/settable-observable.js.map +1 -0
  61. package/lib/node/setting-service.d.ts +5 -1
  62. package/lib/node/setting-service.d.ts.map +1 -1
  63. package/lib/node/setting-service.js +25 -10
  64. package/lib/node/setting-service.js.map +1 -1
  65. package/lib/node/setting-service.spec.d.ts +2 -0
  66. package/lib/node/setting-service.spec.d.ts.map +1 -0
  67. package/lib/node/setting-service.spec.js +84 -0
  68. package/lib/node/setting-service.spec.js.map +1 -0
  69. package/package.json +4 -4
  70. package/src/browser/context-key-service.ts +5 -0
  71. package/src/browser/menu/browser-menu-plugin.ts +2 -2
  72. package/src/browser/saveable-service.ts +4 -0
  73. package/src/browser/tree/tree-widget.tsx +39 -25
  74. package/src/common/array-utils.ts +53 -0
  75. package/src/common/diff.ts +56 -0
  76. package/src/common/message-rpc/msg-pack-extension-manager.ts +2 -2
  77. package/src/common/observable/autorun.ts +246 -0
  78. package/src/common/observable/derived-observable.ts +321 -0
  79. package/src/common/observable/index.ts +23 -0
  80. package/src/common/observable/observable-base.ts +324 -0
  81. package/src/common/observable/observable-from-event.ts +148 -0
  82. package/src/common/observable/observable-signal.ts +48 -0
  83. package/src/common/observable/observable-utils.ts +119 -0
  84. package/src/common/observable/observable.spec.ts +342 -0
  85. package/src/common/observable/settable-observable.ts +73 -0
  86. package/src/node/setting-service.spec.ts +97 -0
  87. package/src/node/setting-service.ts +25 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setting-service.spec.d.ts","sourceRoot":"","sources":["../../src/node/setting-service.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2025 and others.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const chai_1 = require("chai");
19
+ const fs_1 = require("fs");
20
+ const inversify_1 = require("inversify");
21
+ const sinon = require("sinon");
22
+ const env_variables_1 = require("../common/env-variables");
23
+ const logger_1 = require("../common/logger");
24
+ const mock_logger_1 = require("../common/test/mock-logger");
25
+ const uri_1 = require("../common/uri");
26
+ const setting_service_1 = require("./setting-service");
27
+ describe('SettingServiceImpl', () => {
28
+ const mockConfigDirUri = new uri_1.URI('mock');
29
+ const setup = () => {
30
+ const container = new inversify_1.Container({ defaultScope: 'Singleton' });
31
+ container.bind(setting_service_1.SettingServiceImpl).toSelf();
32
+ container.bind(mock_logger_1.MockLogger).toSelf();
33
+ container.bind(logger_1.ILogger).toService(mock_logger_1.MockLogger);
34
+ container.bind(env_variables_1.EnvVariablesServer).toConstantValue({
35
+ getConfigDirUri: () => Promise.resolve(mockConfigDirUri.toString()),
36
+ });
37
+ return container;
38
+ };
39
+ afterEach(() => {
40
+ sinon.restore();
41
+ });
42
+ it('should initialize and read settings file', async () => {
43
+ const container = setup();
44
+ const settingService = container.get(setting_service_1.SettingServiceImpl);
45
+ const mockLogger = container.get(mock_logger_1.MockLogger);
46
+ const readFileStub = sinon.stub(fs_1.promises, 'readFile').resolves(JSON.stringify({ key: 'value' }));
47
+ const infoSpy = sinon.spy(mockLogger, 'info');
48
+ const warnSpy = sinon.spy(mockLogger, 'warn');
49
+ const actual = await settingService.get('key');
50
+ (0, chai_1.expect)(actual).to.be.equal('value');
51
+ (0, chai_1.expect)(readFileStub.calledWith(mockConfigDirUri.resolve('backend-settings.json').path.fsPath())).to.be.true;
52
+ (0, chai_1.expect)(infoSpy.callCount).to.be.equal(0);
53
+ (0, chai_1.expect)(warnSpy.callCount).to.be.equal(0);
54
+ });
55
+ it('should fallback to default and log info when errors with ENOENT', async () => {
56
+ const container = setup();
57
+ const settingService = container.get(setting_service_1.SettingServiceImpl);
58
+ const mockLogger = container.get(mock_logger_1.MockLogger);
59
+ const enoent = Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
60
+ sinon.stub(fs_1.promises, 'readFile').rejects(enoent);
61
+ const infoSpy = sinon.spy(mockLogger, 'info');
62
+ const warnSpy = sinon.spy(mockLogger, 'warn');
63
+ const actual = await settingService.get('key');
64
+ (0, chai_1.expect)(actual).to.be.undefined;
65
+ (0, chai_1.expect)(infoSpy.callCount).to.be.equal(1);
66
+ (0, chai_1.expect)(infoSpy.firstCall.args[0]).to.include('Falling back to defaults');
67
+ (0, chai_1.expect)(warnSpy.callCount).to.be.equal(0);
68
+ });
69
+ it('should fallback to default and log warn when errors', async () => {
70
+ const container = setup();
71
+ const settingService = container.get(setting_service_1.SettingServiceImpl);
72
+ const mockLogger = container.get(mock_logger_1.MockLogger);
73
+ const enoent = Object.assign(new Error('EISDIR'), { code: 'EISDIR' });
74
+ sinon.stub(fs_1.promises, 'readFile').rejects(enoent);
75
+ const infoSpy = sinon.spy(mockLogger, 'info');
76
+ const warnSpy = sinon.spy(mockLogger, 'warn');
77
+ const actual = await settingService.get('key');
78
+ (0, chai_1.expect)(actual).to.be.undefined;
79
+ (0, chai_1.expect)(infoSpy.callCount).to.be.equal(0);
80
+ (0, chai_1.expect)(warnSpy.callCount).to.be.equal(1);
81
+ (0, chai_1.expect)(warnSpy.firstCall.args[0]).to.include('Falling back to defaults');
82
+ });
83
+ });
84
+ //# sourceMappingURL=setting-service.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setting-service.spec.js","sourceRoot":"","sources":["../../src/node/setting-service.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theia/core",
3
- "version": "1.64.0-next.17+58507bbed",
3
+ "version": "1.64.0-next.35+c964c3040",
4
4
  "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.",
5
5
  "main": "lib/common/index.js",
6
6
  "typings": "lib/common/index.d.ts",
@@ -17,8 +17,8 @@
17
17
  "@lumino/virtualdom": "^2.0.2",
18
18
  "@lumino/widgets": "2.5.0",
19
19
  "@parcel/watcher": "^2.5.0",
20
- "@theia/application-package": "1.64.0-next.17+58507bbed",
21
- "@theia/request": "1.64.0-next.17+58507bbed",
20
+ "@theia/application-package": "1.64.0-next.35+c964c3040",
21
+ "@theia/request": "1.64.0-next.35+c964c3040",
22
22
  "@types/body-parser": "^1.16.4",
23
23
  "@types/cookie": "^0.3.3",
24
24
  "@types/express": "^4.17.21",
@@ -216,5 +216,5 @@
216
216
  "nyc": {
217
217
  "extends": "../../configs/nyc.json"
218
218
  },
219
- "gitHead": "58507bbedb95724735981f44f034e7036fa2f19e"
219
+ "gitHead": "c964c30402a92d5ba662fcafa839af99d8760f53"
220
220
  }
@@ -41,6 +41,11 @@ export interface ContextKeyChangeEvent {
41
41
  affects(keys: { has(key: string): boolean }): boolean;
42
42
  }
43
43
 
44
+ export interface Context {
45
+ getValue<T extends ContextKeyValue = ContextKeyValue>(key: string): T | undefined;
46
+ readonly onDidChange?: Event<ContextKeyChangeEvent>;
47
+ }
48
+
44
49
  export const ContextKeyService = Symbol('ContextKeyService');
45
50
 
46
51
  export interface ContextMatcher {
@@ -251,8 +251,8 @@ export class DynamicMenuWidget extends MenuWidget {
251
251
  }
252
252
 
253
253
  protected override onBeforeDetach(msg: Message): void {
254
- this.node.ownerDocument.removeEventListener('pointerdown', this);
255
- super.onAfterDetach(msg);
254
+ this.node.ownerDocument.removeEventListener('pointerdown', this, true);
255
+ super.onBeforeDetach(msg);
256
256
  }
257
257
 
258
258
  override handleEvent(event: Event): void {
@@ -96,6 +96,10 @@ export class SaveableService implements FrontendApplicationContribution {
96
96
  this.onDidAutoSaveChangeEmitter.fire(mode);
97
97
  if (mode === 'onFocusChange') {
98
98
  // If the new mode is onFocusChange, we need to save all dirty documents that are not focused
99
+ if (!this.shell) {
100
+ // Shell is not ready yet, skip auto-saving widgets
101
+ return;
102
+ }
99
103
  const widgets = this.shell.widgets;
100
104
  for (const widget of widgets) {
101
105
  const saveable = Saveable.get(widget);
@@ -21,7 +21,8 @@ import { Key, KeyCode, KeyModifier } from '../keyboard/keys';
21
21
  import { ContextMenuRenderer } from '../context-menu-renderer';
22
22
  import { StatefulWidget } from '../shell';
23
23
  import {
24
- EXPANSION_TOGGLE_CLASS, SELECTED_CLASS, COLLAPSED_CLASS, FOCUS_CLASS, BUSY_CLASS, CODICON_TREE_ITEM_CLASSES, CODICON_LOADING_CLASSES, Widget, UnsafeWidgetUtilities
24
+ EXPANSION_TOGGLE_CLASS, SELECTED_CLASS, COLLAPSED_CLASS, FOCUS_CLASS, BUSY_CLASS, CODICON_TREE_ITEM_CLASSES, CODICON_LOADING_CLASSES, Widget, UnsafeWidgetUtilities,
25
+ addEventListener
25
26
  } from '../widgets';
26
27
  import { TreeNode, CompositeTreeNode } from './tree';
27
28
  import { TreeModel } from './tree-model';
@@ -315,31 +316,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
315
316
  this.updateDecorations();
316
317
  });
317
318
  if (this.props.globalSelection) {
318
- this.toDispose.pushAll([
319
- this.model.onSelectionChanged(() => {
320
- if (this.node.contains(document.activeElement)) {
321
- this.updateGlobalSelection();
322
- }
323
- }),
324
- this.focusService.onDidChangeFocus(focus => {
325
- if (focus && this.node.contains(document.activeElement) && this.model.selectedNodes[0] !== focus && this.model.selectedNodes.includes(focus)) {
326
- this.updateGlobalSelection();
327
- }
328
- }),
329
- Disposable.create(() => {
330
- const selection = this.selectionService.selection;
331
- if (TreeWidgetSelection.isSource(selection, this)) {
332
- this.selectionService.selection = undefined;
333
- }
334
- })
335
- ]);
336
-
337
- this.node.addEventListener('focusin', e => {
338
- if (this.model.selectedNodes.length && (!this.selectionService.selection || !TreeWidgetSelection.isSource(this.selectionService.selection, this))) {
339
- this.updateGlobalSelection();
340
- }
341
- });
319
+ this.registerGlobalSelectionHandlers();
342
320
  }
321
+
343
322
  this.toDispose.push(this.corePreferences.onPreferenceChanged(preference => {
344
323
  if (preference.preferenceName === 'workbench.tree.renderIndentGuides') {
345
324
  this.update();
@@ -347,6 +326,41 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
347
326
  }));
348
327
  }
349
328
 
329
+ protected registerGlobalSelectionHandlers(): void {
330
+ this.model.onSelectionChanged(this.handleGlobalSelectionOnModelSelectionChange, this, this.toDispose);
331
+ this.focusService.onDidChangeFocus(this.handleGlobalSelectionOnFocusServiceFocusChange, this, this.toDispose);
332
+ this.toDispose.push(addEventListener(this.node, 'focusin', this.handleGlobalSelectionOnFocusIn.bind(this)));
333
+ this.toDispose.push(Disposable.create(this.handleGlobalSelectionOnDisposal.bind(this)));
334
+ }
335
+
336
+ protected handleGlobalSelectionOnModelSelectionChange(): void {
337
+ if (this.shouldUpdateGlobalSelection()) {
338
+ this.updateGlobalSelection();
339
+ }
340
+ }
341
+
342
+ protected handleGlobalSelectionOnFocusServiceFocusChange(focus: SelectableTreeNode | undefined): void {
343
+ if (focus && this.shouldUpdateGlobalSelection() && this.model.selectedNodes[0] !== focus && this.model.selectedNodes.includes(focus)) {
344
+ this.updateGlobalSelection();
345
+ }
346
+ }
347
+
348
+ protected handleGlobalSelectionOnFocusIn(): void {
349
+ if (this.model.selectedNodes.length && (!this.selectionService.selection || !TreeWidgetSelection.isSource(this.selectionService.selection, this))) {
350
+ this.updateGlobalSelection();
351
+ }
352
+ }
353
+
354
+ protected handleGlobalSelectionOnDisposal(): void {
355
+ if (TreeWidgetSelection.isSource(this.selectionService.selection, this)) {
356
+ this.selectionService.selection = undefined;
357
+ }
358
+ }
359
+
360
+ protected shouldUpdateGlobalSelection(): boolean {
361
+ return this.node.contains(document.activeElement) || TreeWidgetSelection.isSource(this.selectionService.selection, this);
362
+ }
363
+
350
364
  /**
351
365
  * Update the global selection for the tree.
352
366
  */
@@ -151,4 +151,57 @@ export namespace ArrayUtils {
151
151
  }
152
152
  return true;
153
153
  }
154
+
155
+ export function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
156
+ if (one === other) {
157
+ return true;
158
+ }
159
+
160
+ if (!one || !other) {
161
+ return false;
162
+ }
163
+
164
+ if (one.length !== other.length) {
165
+ return false;
166
+ }
167
+
168
+ for (let i = 0, len = one.length; i < len; i++) {
169
+ if (!itemEquals(one[i], other[i])) {
170
+ return false;
171
+ }
172
+ }
173
+
174
+ return true;
175
+ }
176
+
177
+ export function findLast<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
178
+ const idx = findLastIdx(array, predicate);
179
+ if (idx === -1) {
180
+ return undefined;
181
+ }
182
+ return array[idx];
183
+ }
184
+
185
+ export function findLastIdx<T>(array: readonly T[], predicate: (item: T) => boolean, fromIndex = array.length - 1): number {
186
+ for (let i = fromIndex; i >= 0; i--) {
187
+ const element = array[i];
188
+
189
+ if (predicate(element)) {
190
+ return i;
191
+ }
192
+ }
193
+
194
+ return -1;
195
+ }
196
+
197
+ export function checkAdjacentItems<T>(items: readonly T[], predicate: (item1: T, item2: T) => boolean): boolean {
198
+ for (let i = 0; i < items.length - 1; i++) {
199
+ const a = items[i];
200
+ const b = items[i + 1];
201
+ if (!predicate(a, b)) {
202
+ return false;
203
+ }
204
+ }
205
+ return true;
206
+ }
154
207
  }
@@ -0,0 +1,56 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 1C-Soft LLC 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 { Range } from 'vscode-languageserver-protocol';
18
+ import { CancellationToken } from './cancellation';
19
+ import URI from './uri';
20
+
21
+ /** Represents a textual diff. */
22
+ export interface Diff {
23
+ readonly changes: readonly DetailedLineRangeMapping[];
24
+ }
25
+
26
+ export interface DetailedLineRangeMapping extends LineRangeMapping {
27
+ readonly innerChanges?: readonly RangeMapping[];
28
+ }
29
+
30
+ export interface LineRangeMapping {
31
+ readonly left: LineRange;
32
+ readonly right: LineRange;
33
+ }
34
+
35
+ /** Represents a range of whole lines of text. */
36
+ export interface LineRange {
37
+ /** A zero-based number of the start line. */
38
+ readonly start: number;
39
+ /** A zero-based number of the end line, exclusive. */
40
+ readonly end: number;
41
+ }
42
+
43
+ export interface RangeMapping {
44
+ readonly left: Range;
45
+ readonly right: Range;
46
+ }
47
+
48
+ export const DiffComputer = Symbol('DiffComputer');
49
+
50
+ export interface DiffComputer {
51
+ computeDiff(left: URI, right: URI, options?: DiffComputerOptions): Promise<Diff | undefined>;
52
+ }
53
+
54
+ export interface DiffComputerOptions {
55
+ readonly cancellationToken?: CancellationToken;
56
+ }
@@ -47,8 +47,8 @@ export class MsgPackExtensionManager {
47
47
  addExtension({
48
48
  Class: extension.class,
49
49
  type: extension.tag,
50
- write: extension.serialize,
51
- read: extension.deserialize
50
+ write: instance => extension.serialize(instance),
51
+ read: serialized => extension.deserialize(serialized)
52
52
  });
53
53
  });
54
54
  }
@@ -0,0 +1,246 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 1C-Soft LLC 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
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+ // copied and modified from https://github.com/microsoft/vscode/blob/1.96.3/src/vs/base/common/observableInternal/autorun.ts
21
+
22
+ import { Disposable } from '../disposable';
23
+ import { Observable } from './observable-base';
24
+
25
+ export class Autorun<TChangeSummary = unknown> implements Disposable {
26
+
27
+ protected state = Autorun.State.Stale;
28
+ protected updateCount = 0;
29
+ protected disposed = false;
30
+ protected isRunning = false;
31
+ protected dependencies = new Set<Observable<unknown>>();
32
+ protected dependenciesToBeRemoved?: Set<Observable<unknown>>;
33
+ protected readonly dependencyObserver = this.createDependencyObserver();
34
+ protected readonly createChangeSummary?: () => TChangeSummary;
35
+ protected readonly willHandleChange?: <T, TChange>(context: Observable.ChangeContext<T, TChange>, changeSummary: TChangeSummary | undefined) => boolean;
36
+ protected changeSummary?: TChangeSummary;
37
+
38
+ constructor(
39
+ protected readonly doRun: (args: Autorun.Args<TChangeSummary>) => void,
40
+ options?: Autorun.Options<TChangeSummary>
41
+ ) {
42
+ this.createChangeSummary = options?.createChangeSummary;
43
+ this.willHandleChange = options?.willHandleChange;
44
+ this.changeSummary = this.createChangeSummary?.();
45
+ try {
46
+ this.run(true);
47
+ } catch (e) {
48
+ this.dispose();
49
+ throw e;
50
+ }
51
+ }
52
+
53
+ dispose(): void {
54
+ if (this.disposed) {
55
+ return;
56
+ }
57
+ this.disposed = true;
58
+ for (const dependency of this.dependencies) {
59
+ dependency.removeObserver(this.dependencyObserver);
60
+ }
61
+ this.dependencies.clear();
62
+ }
63
+
64
+ protected run(isFirstRun = false): void {
65
+ if (this.disposed) {
66
+ return;
67
+ }
68
+
69
+ this.dependenciesToBeRemoved = this.dependencies;
70
+ this.dependencies = new Set<Observable<unknown>>();
71
+
72
+ this.state = Autorun.State.UpToDate;
73
+
74
+ try {
75
+ const { changeSummary } = this;
76
+ this.changeSummary = this.createChangeSummary?.();
77
+ this.isRunning = true;
78
+ Observable.Accessor.runWithAccessor(() => this.doRun({ autorun: this, isFirstRun, changeSummary }), dependency => this.watchDependency(dependency));
79
+ } finally {
80
+ this.isRunning = false;
81
+ // We don't want our watched dependencies to think that they are no longer observed, even temporarily.
82
+ // Thus, we only unsubscribe from dependencies that are definitely not watched anymore.
83
+ for (const dependency of this.dependenciesToBeRemoved) {
84
+ dependency.removeObserver(this.dependencyObserver);
85
+ }
86
+ this.dependenciesToBeRemoved = undefined;
87
+ }
88
+ }
89
+
90
+ protected watchDependency<T>(dependency: Observable<T>): T {
91
+ if (!this.isRunning) {
92
+ throw new Error('The accessor may only be called while the autorun is running');
93
+ }
94
+
95
+ // In case the run action disposed the autorun.
96
+ if (this.disposed) {
97
+ return dependency.getUntracked();
98
+ }
99
+
100
+ // Subscribe before getting the value to enable caching.
101
+ dependency.addObserver(this.dependencyObserver);
102
+ // This might call handleChange indirectly, which could invalidate us.
103
+ const value = dependency.getUntracked();
104
+ // Which is why we only add the observable to the dependencies now.
105
+ this.dependencies.add(dependency);
106
+ this.dependenciesToBeRemoved?.delete(dependency);
107
+ return value;
108
+ }
109
+
110
+ protected createDependencyObserver(): Observable.Observer {
111
+ return {
112
+ beginUpdate: () => {
113
+ if (this.state === Autorun.State.UpToDate) {
114
+ this.state = Autorun.State.DependenciesMightHaveChanged;
115
+ }
116
+ this.updateCount++;
117
+ },
118
+
119
+ endUpdate: () => {
120
+ this.updateCount--;
121
+ if (this.updateCount === 0) {
122
+ do {
123
+ if (this.state === Autorun.State.DependenciesMightHaveChanged) {
124
+ this.state = Autorun.State.UpToDate;
125
+ for (const dependency of this.dependencies) {
126
+ dependency.update(); // might call handleChange indirectly, which could make us stale
127
+ if (this.state as Autorun.State === Autorun.State.Stale) {
128
+ // The other dependencies will refresh on demand
129
+ break;
130
+ }
131
+ }
132
+ }
133
+
134
+ if (this.state !== Autorun.State.UpToDate) {
135
+ try {
136
+ this.run();
137
+ } catch (e) {
138
+ console.error(e);
139
+ }
140
+ }
141
+ // In case the run action changed one of our dependencies, we need to run again.
142
+ } while (this.state !== Autorun.State.UpToDate);
143
+ }
144
+ if (this.updateCount < 0) {
145
+ throw new Error('Unexpected update count: ' + this.updateCount);
146
+ }
147
+ },
148
+
149
+ handlePossibleChange: <T>(observable: Observable<T>) => {
150
+ if (this.state === Autorun.State.UpToDate && this.dependencies.has(observable) && !this.dependenciesToBeRemoved?.has(observable)) {
151
+ this.state = Autorun.State.DependenciesMightHaveChanged;
152
+ }
153
+ },
154
+
155
+ handleChange: <T, TChange>(observable: Observable<T, TChange>, change: TChange) => {
156
+ if (this.dependencies.has(observable) && !this.dependenciesToBeRemoved?.has(observable)) {
157
+ let shouldReact = true;
158
+ if (this.willHandleChange) {
159
+ try {
160
+ shouldReact = this.willHandleChange({
161
+ observable,
162
+ change,
163
+ isChangeOf: <U, UChange>(o: Observable<U, UChange>): this is { change: UChange } => o as unknown === observable
164
+ }, this.changeSummary);
165
+ } catch (e) {
166
+ console.error(e);
167
+ }
168
+ }
169
+ if (shouldReact) {
170
+ this.state = Autorun.State.Stale;
171
+ }
172
+ }
173
+ }
174
+ };
175
+ }
176
+ }
177
+
178
+ export namespace Autorun {
179
+
180
+ /**
181
+ * Runs the given {@link run} function immediately, and whenever an update scope ends
182
+ * and an observable tracked as a dependency of the autorun has changed.
183
+ *
184
+ * Note that the run function of the autorun is called within an invocation context where
185
+ * the {@link Observable.Accessor.getCurrent current accessor} is set to track the autorun
186
+ * dependencies, so that any observables accessed with `get()` will automatically be tracked.
187
+ * Occasionally, it might be useful to disable such automatic tracking and track the dependencies
188
+ * manually with `get(accessor)`. This can be done using the {@link Observable.noAutoTracking} function,
189
+ * e.g.
190
+ * ```ts
191
+ * this.toDispose.push(Autorun.create(() => Observable.noAutoTracking(accessor => {
192
+ * const value1 = this.observable1.get(accessor); // the autorun will depend on this observable...
193
+ * const value2 = this.observable2.get(); // ...but not on this observable
194
+ * })));
195
+ * ```
196
+ * In particular, this pattern might be useful when copying existing autorun code from VS Code,
197
+ * where observables can only be tracked manually with `read(reader)`, which corresponds to
198
+ * `get(accessor)` in Theia; calls to `get()` never cause an observable to be tracked. This directly
199
+ * corresponds to disabling automatic tracking in Theia with {@link Observable.noAutoTracking}.
200
+ */
201
+ export function create<TChangeSummary = void>(run: (args: Args<TChangeSummary>) => void, options?: Options<TChangeSummary>): Disposable {
202
+ return new Autorun(run, options);
203
+ }
204
+
205
+ export interface Args<TChangeSummary> {
206
+ readonly autorun: Disposable;
207
+ readonly isFirstRun: boolean;
208
+ /**
209
+ * The change summary with the changes collected from the start of the previous run of the autorun until the start of this run.
210
+ *
211
+ * The change summary is created by {@link Options.createChangeSummary} and
212
+ * the changes are collected by {@link Options.willHandleChange}.
213
+ */
214
+ readonly changeSummary: TChangeSummary | undefined;
215
+ };
216
+
217
+ export interface Options<TChangeSummary> {
218
+ /**
219
+ * Creates a change summary that can collect the changes reported by the observed dependencies to {@link willHandleChange}.
220
+ */
221
+ createChangeSummary?: () => TChangeSummary;
222
+
223
+ /**
224
+ * Handles a change reported by an observed dependency, e.g. by adding it to the {@link changeSummary}.
225
+ * Returns `true` if the reported change should be reacted to, and `false` if it should be ignored.
226
+ */
227
+ willHandleChange?: <T, TChange>(context: Observable.ChangeContext<T, TChange>, changeSummary: TChangeSummary | undefined) => boolean;
228
+ }
229
+
230
+ export const enum State {
231
+ /**
232
+ * Dependencies might have changed. Need to check if at least one dependency has actually changed.
233
+ */
234
+ DependenciesMightHaveChanged,
235
+
236
+ /**
237
+ * A dependency has changed. Need to (re-)run.
238
+ */
239
+ Stale,
240
+
241
+ /**
242
+ * All is up to date.
243
+ */
244
+ UpToDate
245
+ }
246
+ }