@theia/core 1.64.0-next.28 → 1.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/i18n/nls.cs.json +59 -9
- package/i18n/nls.de.json +59 -9
- package/i18n/nls.es.json +59 -9
- package/i18n/nls.fr.json +59 -9
- package/i18n/nls.hu.json +59 -9
- package/i18n/nls.it.json +59 -9
- package/i18n/nls.ja.json +59 -9
- package/i18n/nls.json +63 -13
- package/i18n/nls.ko.json +59 -9
- package/i18n/nls.pl.json +59 -9
- package/i18n/nls.pt-br.json +59 -9
- package/i18n/nls.ru.json +59 -9
- package/i18n/nls.tr.json +59 -9
- package/i18n/nls.zh-cn.json +59 -9
- package/i18n/nls.zh-tw.json +59 -9
- package/lib/browser/catalog.json +149 -15
- package/lib/browser/context-key-service.d.ts +4 -0
- package/lib/browser/context-key-service.d.ts.map +1 -1
- package/lib/browser/context-key-service.js.map +1 -1
- package/lib/browser/frontend-application-module.js +1 -1
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +2 -2
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/preload/i18n-preload-contribution.js +1 -1
- package/lib/browser/preload/i18n-preload-contribution.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts +1 -1
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/common/array-utils.d.ts +4 -0
- package/lib/common/array-utils.d.ts.map +1 -1
- package/lib/common/array-utils.js +47 -0
- package/lib/common/array-utils.js.map +1 -1
- package/lib/common/diff.d.ts +33 -0
- package/lib/common/diff.d.ts.map +1 -0
- package/lib/common/diff.js +20 -0
- package/lib/common/diff.js.map +1 -0
- package/lib/common/message-rpc/msg-pack-extension-manager.js +2 -2
- package/lib/common/message-rpc/msg-pack-extension-manager.js.map +1 -1
- package/lib/common/observable/autorun.d.ts +81 -0
- package/lib/common/observable/autorun.d.ts.map +1 -0
- package/lib/common/observable/autorun.js +194 -0
- package/lib/common/observable/autorun.js.map +1 -0
- package/lib/common/observable/derived-observable.d.ts +71 -0
- package/lib/common/observable/derived-observable.d.ts.map +1 -0
- package/lib/common/observable/derived-observable.js +258 -0
- package/lib/common/observable/derived-observable.js.map +1 -0
- package/lib/common/observable/index.d.ts +8 -0
- package/lib/common/observable/index.d.ts.map +1 -0
- package/lib/common/observable/index.js +26 -0
- package/lib/common/observable/index.js.map +1 -0
- package/lib/common/observable/observable-base.d.ts +181 -0
- package/lib/common/observable/observable-base.d.ts.map +1 -0
- package/lib/common/observable/observable-base.js +183 -0
- package/lib/common/observable/observable-base.js.map +1 -0
- package/lib/common/observable/observable-from-event.d.ts +41 -0
- package/lib/common/observable/observable-from-event.d.ts.map +1 -0
- package/lib/common/observable/observable-from-event.js +115 -0
- package/lib/common/observable/observable-from-event.js.map +1 -0
- package/lib/common/observable/observable-signal.d.ts +9 -0
- package/lib/common/observable/observable-signal.d.ts.map +1 -0
- package/lib/common/observable/observable-signal.js +45 -0
- package/lib/common/observable/observable-signal.js.map +1 -0
- package/lib/common/observable/observable-utils.d.ts +26 -0
- package/lib/common/observable/observable-utils.d.ts.map +1 -0
- package/lib/common/observable/observable-utils.js +98 -0
- package/lib/common/observable/observable-utils.js.map +1 -0
- package/lib/common/observable/observable.spec.d.ts +2 -0
- package/lib/common/observable/observable.spec.d.ts.map +1 -0
- package/lib/common/observable/observable.spec.js +301 -0
- package/lib/common/observable/observable.spec.js.map +1 -0
- package/lib/common/observable/settable-observable.d.ts +16 -0
- package/lib/common/observable/settable-observable.d.ts.map +1 -0
- package/lib/common/observable/settable-observable.js +58 -0
- package/lib/common/observable/settable-observable.js.map +1 -0
- package/package.json +7 -8
- package/src/browser/context-key-service.ts +5 -0
- package/src/browser/frontend-application-module.ts +1 -1
- package/src/browser/menu/browser-menu-plugin.ts +3 -3
- package/src/browser/preload/i18n-preload-contribution.ts +1 -1
- package/src/browser/shell/application-shell.ts +1 -1
- package/src/common/array-utils.ts +53 -0
- package/src/common/diff.ts +56 -0
- package/src/common/message-rpc/msg-pack-extension-manager.ts +2 -2
- package/src/common/observable/autorun.ts +246 -0
- package/src/common/observable/derived-observable.ts +321 -0
- package/src/common/observable/index.ts +23 -0
- package/src/common/observable/observable-base.ts +324 -0
- package/src/common/observable/observable-from-event.ts +148 -0
- package/src/common/observable/observable-signal.ts +48 -0
- package/src/common/observable/observable-utils.ts +119 -0
- package/src/common/observable/observable.spec.ts +342 -0
- package/src/common/observable/settable-observable.ts +73 -0
|
@@ -212,7 +212,7 @@ export class MenuServices {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
export interface MenuWidgetFactory {
|
|
215
|
-
createMenuWidget(effectiveMenuPath: MenuPath, menu: Submenu, contextMatcher: ContextMatcher, options: BrowserMenuOptions): MenuWidget;
|
|
215
|
+
createMenuWidget(effectiveMenuPath: MenuPath, menu: Submenu, contextMatcher: ContextMatcher, options: BrowserMenuOptions, args?: unknown[]): MenuWidget;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/**
|
|
@@ -252,7 +252,7 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
252
252
|
|
|
253
253
|
protected override onBeforeDetach(msg: Message): void {
|
|
254
254
|
this.node.ownerDocument.removeEventListener('pointerdown', this, true);
|
|
255
|
-
super.
|
|
255
|
+
super.onBeforeDetach(msg);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
override handleEvent(event: Event): void {
|
|
@@ -325,7 +325,7 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
325
325
|
if (node.isVisible(nodePath, contextMatcher, context, ...(this.args || []))) {
|
|
326
326
|
if (CompoundMenuNode.is(node)) {
|
|
327
327
|
if (RenderedMenuNode.is(node)) {
|
|
328
|
-
const submenu = this.services.menuWidgetFactory.createMenuWidget(nodePath, node, this.contextMatcher, this.options);
|
|
328
|
+
const submenu = this.services.menuWidgetFactory.createMenuWidget(nodePath, node, this.contextMatcher, this.options, this.args);
|
|
329
329
|
if (submenu.items.length > 0) {
|
|
330
330
|
result.push({ type: 'submenu', submenu });
|
|
331
331
|
}
|
|
@@ -43,7 +43,7 @@ export class I18nPreloadContribution implements PreloadContribution {
|
|
|
43
43
|
const localization = await this.localizationServer.loadLocalization(locale);
|
|
44
44
|
if (localization.languagePack) {
|
|
45
45
|
nls.localization = localization;
|
|
46
|
-
} else {
|
|
46
|
+
} else if (locale !== nls.defaultLocale) {
|
|
47
47
|
// In case the localization that we've loaded doesn't localize Theia completely (languagePack is false)
|
|
48
48
|
// We simply reset the locale to the default again
|
|
49
49
|
Object.assign(nls, {
|
|
@@ -656,7 +656,7 @@ export class ApplicationShell extends Widget {
|
|
|
656
656
|
this.additionalDraggedUris = undefined;
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
-
|
|
659
|
+
static getDraggedEditorUris(dataTransfer: DataTransfer): URI[] {
|
|
660
660
|
const data = dataTransfer.getData('theia-editor-dnd');
|
|
661
661
|
return data ? data.split('\n').map(entry => new URI(entry)) : [];
|
|
662
662
|
}
|
|
@@ -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
|
+
}
|