@rs-x/state-manager 0.4.11 → 0.4.13
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/dist/index.d.ts +52 -47
- package/dist/index.js +323 -300
- package/package.json +2 -2
- package/readme.md +1125 -960
package/readme.md
CHANGED
|
@@ -7,9 +7,9 @@ A context can be an object, and an index can be a property name — but it is no
|
|
|
7
7
|
|
|
8
8
|
Examples of state indexes:
|
|
9
9
|
|
|
10
|
-
- Object property or field → index = property or field name
|
|
11
|
-
- Array item → index = numeric position
|
|
12
|
-
- Map item → index = map key
|
|
10
|
+
- Object property or field → index = property or field name
|
|
11
|
+
- Array item → index = numeric position
|
|
12
|
+
- Map item → index = map key
|
|
13
13
|
|
|
14
14
|
The State Manager does **not** automatically know how to detect changes for every state value data type, nor how to patch the corresponding state setter. Therefore, it relies on two services:
|
|
15
15
|
|
|
@@ -19,45 +19,38 @@ The State Manager does **not** automatically know how to detect changes for ever
|
|
|
19
19
|
- A service implementing `IIndexValueAccessor`
|
|
20
20
|
Responsible for retrieving the current value.
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
The **State Manager** has the followng interface:
|
|
23
|
+
|
|
24
24
|
```ts
|
|
25
25
|
export interface IStateManager {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setState<T>(
|
|
55
|
-
context: unknown,
|
|
56
|
-
index: unknown,
|
|
57
|
-
value: T
|
|
58
|
-
): void;
|
|
59
|
-
|
|
60
|
-
clear(): void;
|
|
26
|
+
readonly changed: Observable<IStateChange>;
|
|
27
|
+
readonly contextChanged: Observable<IContextChanged>;
|
|
28
|
+
readonly startChangeCycle: Observable<void>;
|
|
29
|
+
readonly endChangeCycle: Observable<void>;
|
|
30
|
+
|
|
31
|
+
isWatched(
|
|
32
|
+
context: unknown,
|
|
33
|
+
index: unknown,
|
|
34
|
+
mustProxify: MustProxify,
|
|
35
|
+
): boolean;
|
|
36
|
+
|
|
37
|
+
watchState(
|
|
38
|
+
context: unknown,
|
|
39
|
+
index: unknown,
|
|
40
|
+
mustProxify?: MustProxify,
|
|
41
|
+
): unknown;
|
|
42
|
+
|
|
43
|
+
releaseState(
|
|
44
|
+
oontext: unknown,
|
|
45
|
+
index: unknown,
|
|
46
|
+
mustProxify?: MustProxify,
|
|
47
|
+
): void;
|
|
48
|
+
|
|
49
|
+
getState<T>(context: unknown, index: unknown): T;
|
|
50
|
+
|
|
51
|
+
setState<T>(context: unknown, index: unknown, value: T): void;
|
|
52
|
+
|
|
53
|
+
clear(): void;
|
|
61
54
|
}
|
|
62
55
|
```
|
|
63
56
|
|
|
@@ -66,12 +59,14 @@ export interface IStateManager {
|
|
|
66
59
|
## Members
|
|
67
60
|
|
|
68
61
|
### **changed**
|
|
62
|
+
|
|
69
63
|
**Type:** `Observable<IStateChange>`
|
|
70
64
|
Emits whenever a state item value changes.
|
|
71
65
|
|
|
72
66
|
---
|
|
73
67
|
|
|
74
68
|
### **contextChanged**
|
|
69
|
+
|
|
75
70
|
**Type:** `Observable<IContextChanged>`
|
|
76
71
|
Emits whenever an entire context is replaced.
|
|
77
72
|
This happens, for example, when a nested object is replaced.
|
|
@@ -79,38 +74,42 @@ This happens, for example, when a nested object is replaced.
|
|
|
79
74
|
---
|
|
80
75
|
|
|
81
76
|
### **startChangeCycle**
|
|
77
|
+
|
|
82
78
|
**Type:** `Observable<void>`
|
|
83
79
|
Emits at the start of processing a state item change.
|
|
84
80
|
|
|
85
81
|
---
|
|
86
82
|
|
|
87
83
|
### **endChangeCycle**
|
|
84
|
+
|
|
88
85
|
**Type:** `Observable<void>`
|
|
89
86
|
Emits at the end of processing a state item change.
|
|
90
87
|
|
|
91
88
|
---
|
|
92
89
|
|
|
93
90
|
### **isWatched(context, index, mustProxify)**
|
|
91
|
+
|
|
94
92
|
Returns whether the state item identified by the `(context, index, mustProxify)` triplet is currently being watched.
|
|
95
93
|
|
|
96
|
-
| Parameter | Type | Description
|
|
97
|
-
| --------------- | -------------------------- |
|
|
98
|
-
| **context** | `unknown` | The context to which the state index belongs.
|
|
99
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
100
|
-
| **mustProxify** | `MustProxify`
|
|
94
|
+
| Parameter | Type | Description |
|
|
95
|
+
| --------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| **context** | `unknown` | The context to which the state index belongs. |
|
|
97
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
98
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. It should be the same predicate that was passed to `watchState`. |
|
|
101
99
|
|
|
102
100
|
**Returns:** `boolean`
|
|
103
101
|
|
|
104
102
|
---
|
|
105
103
|
|
|
106
104
|
### **watchState(context, index, mustProxify?)**
|
|
105
|
+
|
|
107
106
|
Watches a state item identified by the `(context, index, mustProxify)` triplet so that its value is proxified and tracked.
|
|
108
107
|
|
|
109
|
-
| Parameter | Type | Description
|
|
110
|
-
| --------------- | -------------------------- |
|
|
111
|
-
| **context** | `unknown` | The state context.
|
|
112
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
113
|
-
| **mustProxify** | `MustProxify`
|
|
108
|
+
| Parameter | Type | Description |
|
|
109
|
+
| --------------- | -------------------------- | --------------------------------------------------------------------- |
|
|
110
|
+
| **context** | `unknown` | The state context. |
|
|
111
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
112
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. |
|
|
114
113
|
|
|
115
114
|
**Returns:**
|
|
116
115
|
`unknown` — the state item value if it was already being watched; otherwise `undefined`.
|
|
@@ -118,46 +117,50 @@ Watches a state item identified by the `(context, index, mustProxify)` triplet s
|
|
|
118
117
|
---
|
|
119
118
|
|
|
120
119
|
### **releaseState(context, index, mustProxify?)**
|
|
120
|
+
|
|
121
121
|
Releases the state item identified by the `(context, index, mustProxify)` triplet.
|
|
122
122
|
Each call to `watchState` should have a corresponding `releaseState` call to ensure the state item is released when it is no longer needed.
|
|
123
123
|
|
|
124
|
-
| Parameter | Type | Description
|
|
125
|
-
| --------------- | -------------------------- |
|
|
126
|
-
| **context** | `unknown` | The state context.
|
|
127
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
128
|
-
| **mustProxify** | `MustProxify`
|
|
124
|
+
| Parameter | Type | Description |
|
|
125
|
+
| --------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| **context** | `unknown` | The state context. |
|
|
127
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
128
|
+
| **mustProxify** | `MustProxify` _(optional)_ | Predicate determining whether a nested state value must be proxified. It should be the same predicate that was passed to `watchState`. . |
|
|
129
129
|
|
|
130
130
|
**Returns:** `void`
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
134
|
### **getState(context, index)**
|
|
135
|
+
|
|
135
136
|
Retrieves the current state item value identified by the `(context, index)` pair.
|
|
136
137
|
|
|
137
|
-
| Parameter | Type | Description
|
|
138
|
-
| ----------- | --------- |
|
|
139
|
-
| **context** | `unknown` | The state context.
|
|
140
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
138
|
+
| Parameter | Type | Description |
|
|
139
|
+
| ----------- | --------- | ----------------------------------------------------- |
|
|
140
|
+
| **context** | `unknown` | The state context. |
|
|
141
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
141
142
|
|
|
142
143
|
**Returns:** `unknown`
|
|
143
144
|
|
|
144
145
|
---
|
|
145
146
|
|
|
146
147
|
### **setState(context, index, value)**
|
|
148
|
+
|
|
147
149
|
Sets the value of the state item identified by the `(context, index)` pair.
|
|
148
150
|
|
|
149
151
|
Unlike `watchState`, `setState` does **not** track changes. It does not patch setters or proxify values.
|
|
150
152
|
A change event is emitted on the first `setState` call and again whenever the value changes in subsequent calls.
|
|
151
153
|
|
|
152
|
-
| Parameter | Type | Description
|
|
153
|
-
| ----------- | --------- |
|
|
154
|
-
| **context** | `unknown` | The state context.
|
|
155
|
-
| **index** | `unknown` | The index identifying the state on the given context.
|
|
156
|
-
| **value** | `unknown` | The state value.
|
|
154
|
+
| Parameter | Type | Description |
|
|
155
|
+
| ----------- | --------- | ----------------------------------------------------- |
|
|
156
|
+
| **context** | `unknown` | The state context. |
|
|
157
|
+
| **index** | `unknown` | The index identifying the state on the given context. |
|
|
158
|
+
| **value** | `unknown` | The state value. |
|
|
157
159
|
|
|
158
160
|
---
|
|
159
161
|
|
|
160
162
|
### **clear()**
|
|
163
|
+
|
|
161
164
|
Releases all registered state items.
|
|
162
165
|
|
|
163
166
|
**Returns:** `void`
|
|
@@ -183,10 +186,13 @@ There are two ways to get an instance:
|
|
|
183
186
|
|
|
184
187
|
```ts
|
|
185
188
|
import { InjectionContainer } from '@rs-x/core';
|
|
186
|
-
import {
|
|
189
|
+
import {
|
|
190
|
+
IIStateManager,
|
|
191
|
+
RsXStateManagerInjectionTokens,
|
|
192
|
+
} from '@rs-x/state-manager';
|
|
187
193
|
|
|
188
194
|
const stateManager: IIStateManager = InjectionContainer.get(
|
|
189
|
-
|
|
195
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
190
196
|
);
|
|
191
197
|
```
|
|
192
198
|
|
|
@@ -194,14 +200,16 @@ const stateManager: IIStateManager = InjectionContainer.get(
|
|
|
194
200
|
|
|
195
201
|
```ts
|
|
196
202
|
import { Inject } from '@rs-x/core';
|
|
197
|
-
import {
|
|
203
|
+
import {
|
|
204
|
+
IIStateManager,
|
|
205
|
+
RsXStateManagerInjectionTokens,
|
|
206
|
+
} from '@rs-x/state-manager';
|
|
198
207
|
|
|
199
208
|
export class MyClass {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
) {}
|
|
209
|
+
constructor(
|
|
210
|
+
@Inject(RsXStateManagerInjectionTokens.IStateManager)
|
|
211
|
+
private readonly _stateManager: IIStateManager,
|
|
212
|
+
) {}
|
|
205
213
|
}
|
|
206
214
|
```
|
|
207
215
|
|
|
@@ -211,60 +219,61 @@ export class MyClass {
|
|
|
211
219
|
|
|
212
220
|
There are two variants:
|
|
213
221
|
|
|
214
|
-
### Non-recursive
|
|
222
|
+
### Non-recursive
|
|
223
|
+
|
|
215
224
|
Monitors only assignment of a **new value** to the index.
|
|
216
225
|
|
|
217
226
|
```ts
|
|
218
227
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
219
228
|
import {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
type IStateChange,
|
|
230
|
+
type IStateManager,
|
|
231
|
+
RsXStateManagerInjectionTokens,
|
|
232
|
+
RsXStateManagerModule,
|
|
224
233
|
} from '@rs-x/state-manager';
|
|
225
234
|
|
|
226
235
|
// Load the state manager module into the injection container
|
|
227
236
|
InjectionContainer.load(RsXStateManagerModule);
|
|
228
237
|
|
|
229
238
|
export const run = (() => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
240
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const model = {
|
|
244
|
+
x: { y: 10 },
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// This will emit a change event with the initial (current) value.
|
|
248
|
+
console.log('Initial value:');
|
|
249
|
+
const changedSubsription = stateManager.changed.subscribe(
|
|
250
|
+
(change: IStateChange) => {
|
|
251
|
+
printValue(change.newValue);
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// This will emit the new value { y: 10 }
|
|
257
|
+
stateManager.watchState(model, 'x');
|
|
258
|
+
|
|
259
|
+
console.log('Changed value:');
|
|
260
|
+
// This will emit the new value { y: 10 }
|
|
261
|
+
model.x = {
|
|
262
|
+
y: 20,
|
|
236
263
|
};
|
|
237
264
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// This will emit the new value { y: 10 }
|
|
250
|
-
stateContext.x = {
|
|
251
|
-
y: 20
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
console.log(`Latest value:`);
|
|
255
|
-
printValue(stateManager.getState(stateContext, 'x'));
|
|
256
|
-
|
|
257
|
-
// This will emit no change because the state is not recursive.
|
|
258
|
-
console.log('\nstateContext.x.y = 30 will not emit any change:\n---\n');
|
|
259
|
-
stateContext.x.y = 30;
|
|
260
|
-
|
|
261
|
-
} finally {
|
|
262
|
-
changedSubsription.unsubscribe();
|
|
263
|
-
// Always release the state when it is no longer needed.
|
|
264
|
-
stateManager.releaseState(stateContext, 'x');
|
|
265
|
-
}
|
|
265
|
+
console.log(`Latest value:`);
|
|
266
|
+
printValue(stateManager.getState(model, 'x'));
|
|
267
|
+
|
|
268
|
+
// This will emit no change because the state is not recursive.
|
|
269
|
+
console.log('\nmodel.x.y = 30 will not emit any change:\n---\n');
|
|
270
|
+
model.x.y = 30;
|
|
271
|
+
} finally {
|
|
272
|
+
changedSubsription.unsubscribe();
|
|
273
|
+
// Always release the state when it is no longer needed.
|
|
274
|
+
stateManager.releaseState(model, 'x');
|
|
275
|
+
}
|
|
266
276
|
})();
|
|
267
|
-
|
|
268
277
|
```
|
|
269
278
|
|
|
270
279
|
**Output:**
|
|
@@ -290,61 +299,64 @@ stateContext.x.y = 30 will not emit any change:
|
|
|
290
299
|
|
|
291
300
|
---
|
|
292
301
|
|
|
293
|
-
### Recursive
|
|
294
|
-
|
|
302
|
+
### Recursive
|
|
303
|
+
|
|
304
|
+
Monitors assignments **and** changes _inside_ the value.
|
|
295
305
|
Example: if the value is an object, internal object changes are also observed. You can make a state item recursive by passing in a **mustProxify** predicate to a **watchState** call. The mustProxify will be called for every nested index. If you return true it will watch the index otherwise not.
|
|
296
306
|
|
|
297
307
|
```ts
|
|
298
|
-
import { InjectionContainer, printValue
|
|
308
|
+
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
299
309
|
import {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
310
|
+
type IStateChange,
|
|
311
|
+
type IStateManager,
|
|
312
|
+
RsXStateManagerInjectionTokens,
|
|
313
|
+
RsXStateManagerModule,
|
|
314
|
+
watchIndexRecursiveRule,
|
|
304
315
|
} from '@rs-x/state-manager';
|
|
305
316
|
|
|
306
317
|
// Load the state manager module into the injection container
|
|
307
318
|
InjectionContainer.load(RsXStateManagerModule);
|
|
308
319
|
|
|
309
320
|
export const run = (() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
321
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
322
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
323
|
+
);
|
|
324
|
+
const model = {
|
|
325
|
+
x: { y: 10 },
|
|
326
|
+
};
|
|
327
|
+
const changedSubscription = stateManager.changed.subscribe(
|
|
328
|
+
(change: IStateChange) => {
|
|
329
|
+
printValue(change.newValue);
|
|
330
|
+
},
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
// We register recursive state by passing
|
|
335
|
+
// a predicate as the third argument.
|
|
336
|
+
// In this case, we want to watch the entire value,
|
|
337
|
+
// so we pass a predicate that always returns true.
|
|
338
|
+
// This will emit an initial value { y: 10 }
|
|
339
|
+
console.log('Initial value:');
|
|
340
|
+
stateManager.watchState(model, 'x', watchIndexRecursiveRule);
|
|
341
|
+
|
|
342
|
+
console.log('Changed value:');
|
|
343
|
+
// This will emit the new value { y: 10 }
|
|
344
|
+
model.x = {
|
|
345
|
+
y: 20,
|
|
315
346
|
};
|
|
316
|
-
const changedSubscription = stateManager.changed.subscribe((change: IStateChange) => {
|
|
317
|
-
printValue(change.newValue)
|
|
318
|
-
});
|
|
319
347
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
y: 20
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
console.log('Changed (recursive) value:');
|
|
336
|
-
// This will emit the new value { y: 30 } because x
|
|
337
|
-
// is registered as a recursive state.
|
|
338
|
-
stateContext.x.y = 30;
|
|
339
|
-
|
|
340
|
-
console.log(`Latest value:`);
|
|
341
|
-
printValue(stateManager.getState(stateContext, 'x'));
|
|
342
|
-
|
|
343
|
-
} finally {
|
|
344
|
-
changedSubscription.unsubscribe();
|
|
345
|
-
// Always release the state when it is no longer needed.
|
|
346
|
-
stateManager.releaseState(stateContext, 'x', truePredicate);
|
|
347
|
-
}
|
|
348
|
+
console.log('Changed (recursive) value:');
|
|
349
|
+
// This will emit the new value { y: 30 } because x
|
|
350
|
+
// is registered as a recursive state.
|
|
351
|
+
model.x.y = 30;
|
|
352
|
+
|
|
353
|
+
console.log(`Latest value:`);
|
|
354
|
+
printValue(stateManager.getState(model, 'x'));
|
|
355
|
+
} finally {
|
|
356
|
+
changedSubscription.unsubscribe();
|
|
357
|
+
// Always release the state when it is no longer needed.
|
|
358
|
+
stateManager.releaseState(model, 'x', watchIndexRecursiveRule);
|
|
359
|
+
}
|
|
348
360
|
})();
|
|
349
361
|
```
|
|
350
362
|
|
|
@@ -370,82 +382,87 @@ Besides that you can register a watched stated (calling `watchedState`) you can
|
|
|
370
382
|
```ts
|
|
371
383
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
372
384
|
import {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
385
|
+
type IStateChange,
|
|
386
|
+
type IStateManager,
|
|
387
|
+
RsXStateManagerInjectionTokens,
|
|
388
|
+
RsXStateManagerModule,
|
|
377
389
|
} from '@rs-x/state-manager';
|
|
378
390
|
|
|
379
391
|
// Load the state manager module into the injection container
|
|
380
392
|
InjectionContainer.load(RsXStateManagerModule);
|
|
381
393
|
|
|
382
394
|
export const run = (() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
395
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
396
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
397
|
+
);
|
|
386
398
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
399
|
+
class MyModel {
|
|
400
|
+
private readonly _aPlusBId = 'a+b';
|
|
401
|
+
private _a = 10;
|
|
402
|
+
private _b = 20;
|
|
391
403
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
public dispose(): void {
|
|
397
|
-
return stateManager.releaseState(this, this._aPlusBId);
|
|
398
|
-
}
|
|
404
|
+
constructor() {
|
|
405
|
+
this.setAPlusB();
|
|
406
|
+
}
|
|
399
407
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
408
|
+
public dispose(): void {
|
|
409
|
+
return stateManager.releaseState(this, this._aPlusBId);
|
|
410
|
+
}
|
|
403
411
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
412
|
+
public get aPlusB(): number {
|
|
413
|
+
return stateManager.getState(this, this._aPlusBId);
|
|
414
|
+
}
|
|
407
415
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
416
|
+
public get a(): number {
|
|
417
|
+
return this._a;
|
|
418
|
+
}
|
|
412
419
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
420
|
+
public set a(value: number) {
|
|
421
|
+
this._a = value;
|
|
422
|
+
this.setAPlusB();
|
|
423
|
+
}
|
|
416
424
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
425
|
+
public get b(): number {
|
|
426
|
+
return this._b;
|
|
427
|
+
}
|
|
421
428
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
429
|
+
public set b(value: number) {
|
|
430
|
+
this._b = value;
|
|
431
|
+
this.setAPlusB();
|
|
425
432
|
}
|
|
426
433
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
434
|
+
private setAPlusB(): void {
|
|
435
|
+
stateManager.setState(this, this._aPlusBId, this._a + this._b);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
431
438
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
439
|
+
const model = new MyModel();
|
|
440
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
441
|
+
(change: IStateChange) => {
|
|
442
|
+
printValue(change.newValue);
|
|
443
|
+
},
|
|
444
|
+
);
|
|
435
445
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
446
|
+
try {
|
|
447
|
+
console.log(`Initial value for readonly property 'aPlusB':`);
|
|
448
|
+
console.log(model.aPlusB);
|
|
439
449
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
450
|
+
console.log(
|
|
451
|
+
`set 'model.a' to '100' will emit a change event for readonly property 'aPlusB'`,
|
|
452
|
+
);
|
|
453
|
+
console.log(`Changed value for readonly property 'aPlusB':`);
|
|
454
|
+
model.a = 100;
|
|
443
455
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
456
|
+
console.log(
|
|
457
|
+
`set 'model.b' to '200' will emit a change event for readonly property 'aPlusB'`,
|
|
458
|
+
);
|
|
459
|
+
console.log(`Changed value for readonly property 'aPlusB':`);
|
|
460
|
+
model.b = 200;
|
|
461
|
+
} finally {
|
|
462
|
+
changeSubscription.unsubscribe();
|
|
463
|
+
// Always release the state when it is no longer needed.
|
|
464
|
+
model.dispose();
|
|
465
|
+
}
|
|
449
466
|
})();
|
|
450
467
|
```
|
|
451
468
|
|
|
@@ -476,52 +493,57 @@ When done, release the state:
|
|
|
476
493
|
```ts
|
|
477
494
|
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
478
495
|
import {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
496
|
+
type IStateChange,
|
|
497
|
+
type IStateManager,
|
|
498
|
+
RsXStateManagerInjectionTokens,
|
|
499
|
+
RsXStateManagerModule,
|
|
483
500
|
} from '@rs-x/state-manager';
|
|
484
501
|
|
|
485
502
|
// Load the state manager module into the injection container
|
|
486
503
|
InjectionContainer.load(RsXStateManagerModule);
|
|
487
504
|
|
|
488
505
|
export const run = (() => {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
stateContext.x = { y: 20 };
|
|
506
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
507
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
508
|
+
);
|
|
509
|
+
const model = {
|
|
510
|
+
x: { y: 10 },
|
|
511
|
+
};
|
|
512
|
+
const changedSubscription = stateManager.changed.subscribe(
|
|
513
|
+
(change: IStateChange) => {
|
|
514
|
+
printValue(change.newValue);
|
|
515
|
+
},
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
// Register is idempotent: you can register the same state multiple times.
|
|
520
|
+
// For every register call, make sure you call unregister when you're done.
|
|
521
|
+
console.log('Initial value:');
|
|
522
|
+
stateManager.watchState(model, 'x');
|
|
523
|
+
stateManager.watchState(model, 'x');
|
|
508
524
|
|
|
509
|
-
|
|
525
|
+
console.log('Changed value:');
|
|
526
|
+
model.x = { y: 20 };
|
|
510
527
|
|
|
511
|
-
|
|
512
|
-
console.log('Changed value:');
|
|
513
|
-
stateContext.x = { y: 30 };
|
|
528
|
+
stateManager.releaseState(model, 'x');
|
|
514
529
|
|
|
515
|
-
|
|
530
|
+
console.log(
|
|
531
|
+
'Changed event is still emitted after unregister because one observer remains.',
|
|
532
|
+
);
|
|
533
|
+
console.log('Changed value:');
|
|
534
|
+
model.x = { y: 30 };
|
|
516
535
|
|
|
517
|
-
|
|
518
|
-
console.log('Changed value:');
|
|
519
|
-
console.log('---');
|
|
520
|
-
stateContext.x = { y: 30 };
|
|
536
|
+
stateManager.releaseState(model, 'x');
|
|
521
537
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
538
|
+
console.log(
|
|
539
|
+
'Changed event is no longer emitted after the last observer unregisters.',
|
|
540
|
+
);
|
|
541
|
+
console.log('Changed value:');
|
|
542
|
+
console.log('---');
|
|
543
|
+
model.x = { y: 30 };
|
|
544
|
+
} finally {
|
|
545
|
+
changedSubscription.unsubscribe();
|
|
546
|
+
}
|
|
525
547
|
})();
|
|
526
548
|
```
|
|
527
549
|
|
|
@@ -560,7 +582,7 @@ You can override this factory list by providing your own custom provider service
|
|
|
560
582
|
| Observable | Not indexable | Subscribe | [example](#observable) |
|
|
561
583
|
| Custom type | user defined | user defined | [example](#customtype) |
|
|
562
584
|
|
|
563
|
-
State item is identified by a **context**,
|
|
585
|
+
State item is identified by a **context**, **index** and **mustProxify** predicate for a recursive state item
|
|
564
586
|
The manager checks each observer factory to determine support based on the **context** and **index**.
|
|
565
587
|
|
|
566
588
|
Behavior:
|
|
@@ -571,86 +593,92 @@ Behavior:
|
|
|
571
593
|
The following example illustrates the different state types:
|
|
572
594
|
|
|
573
595
|
### Object property/field
|
|
596
|
+
|
|
574
597
|
**Example**
|
|
598
|
+
|
|
575
599
|
```ts
|
|
576
|
-
import { InjectionContainer, printValue,
|
|
600
|
+
import { InjectionContainer, printValue, Type } from '@rs-x/core';
|
|
577
601
|
import {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
602
|
+
type IStateChange,
|
|
603
|
+
type IStateManager,
|
|
604
|
+
RsXStateManagerInjectionTokens,
|
|
605
|
+
RsXStateManagerModule,
|
|
606
|
+
watchIndexRecursiveRule,
|
|
582
607
|
} from '@rs-x/state-manager';
|
|
583
608
|
|
|
584
609
|
// Load the state manager module into the injection container
|
|
585
610
|
InjectionContainer.load(RsXStateManagerModule);
|
|
586
611
|
|
|
587
612
|
interface INestStateConext {
|
|
588
|
-
|
|
589
|
-
|
|
613
|
+
a: number;
|
|
614
|
+
nested?: INestStateConext;
|
|
590
615
|
}
|
|
591
616
|
|
|
592
|
-
class
|
|
593
|
-
|
|
594
|
-
|
|
617
|
+
class MyModel {
|
|
618
|
+
private _b: INestStateConext = {
|
|
619
|
+
a: 10,
|
|
620
|
+
nested: {
|
|
621
|
+
a: 20,
|
|
622
|
+
nested: {
|
|
623
|
+
a: 30,
|
|
595
624
|
nested: {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
public set b(value: INestStateConext) {
|
|
611
|
-
this._b = value;
|
|
612
|
-
}
|
|
625
|
+
a: 40,
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
public get b(): INestStateConext {
|
|
632
|
+
return this._b;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
public set b(value: INestStateConext) {
|
|
636
|
+
this._b = value;
|
|
637
|
+
}
|
|
613
638
|
}
|
|
614
639
|
|
|
615
640
|
export const run = (() => {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
641
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
642
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
643
|
+
);
|
|
619
644
|
|
|
620
|
-
|
|
645
|
+
const model = new MyModel();
|
|
621
646
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
647
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
648
|
+
(change: IStateChange) => {
|
|
649
|
+
printValue(change.newValue);
|
|
650
|
+
},
|
|
651
|
+
);
|
|
625
652
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
653
|
+
try {
|
|
654
|
+
// Observe property `b` recursively.
|
|
655
|
+
// Otherwise, only assigning a new value to model.b would emit a change event.
|
|
656
|
+
// This will emit a change event with the initial (current) value.
|
|
657
|
+
console.log('Initial value:');
|
|
658
|
+
stateManager.watchState(model, 'b', watchIndexRecursiveRule);
|
|
632
659
|
|
|
633
|
-
|
|
634
|
-
|
|
660
|
+
console.log('\nReplacing model.b.nested.nested will emit a change event');
|
|
661
|
+
console.log('Changed value:');
|
|
635
662
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
console.log(`Latest value:`);
|
|
644
|
-
printValue(stateManager.getState(stateContext, 'b'));
|
|
663
|
+
(Type.toObject(model.b.nested) ?? {}).nested = {
|
|
664
|
+
a: -30,
|
|
665
|
+
nested: {
|
|
666
|
+
a: -40,
|
|
667
|
+
},
|
|
668
|
+
};
|
|
645
669
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
670
|
+
console.log(`Latest value:`);
|
|
671
|
+
printValue(stateManager.getState(model, 'b'));
|
|
672
|
+
} finally {
|
|
673
|
+
changeSubscription.unsubscribe();
|
|
674
|
+
// Always release the state when it is no longer needed.
|
|
675
|
+
stateManager.releaseState(model, 'b', watchIndexRecursiveRule);
|
|
676
|
+
}
|
|
651
677
|
})();
|
|
652
678
|
```
|
|
679
|
+
|
|
653
680
|
**Output:**
|
|
681
|
+
|
|
654
682
|
```console
|
|
655
683
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-property.ts
|
|
656
684
|
Initial value:
|
|
@@ -697,86 +725,98 @@ Latest value:
|
|
|
697
725
|
```
|
|
698
726
|
|
|
699
727
|
### Date
|
|
728
|
+
|
|
700
729
|
**Example**
|
|
730
|
+
|
|
701
731
|
```ts
|
|
702
|
-
import { InjectionContainer,
|
|
732
|
+
import { InjectionContainer, utCDate } from '@rs-x/core';
|
|
703
733
|
import {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
734
|
+
type IProxyRegistry,
|
|
735
|
+
type IStateChange,
|
|
736
|
+
type IStateManager,
|
|
737
|
+
RsXStateManagerInjectionTokens,
|
|
738
|
+
RsXStateManagerModule,
|
|
739
|
+
watchIndexRecursiveRule,
|
|
709
740
|
} from '@rs-x/state-manager';
|
|
710
741
|
|
|
711
742
|
// Load the state manager module into the injection container
|
|
712
743
|
InjectionContainer.load(RsXStateManagerModule);
|
|
713
744
|
|
|
714
745
|
function watchDate(stateManager: IStateManager) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
746
|
+
console.log('\n******************************************');
|
|
747
|
+
console.log('* Watching date');
|
|
748
|
+
console.log('******************************************\n');
|
|
749
|
+
|
|
750
|
+
const model = {
|
|
751
|
+
date: utCDate(2021, 2, 5),
|
|
752
|
+
};
|
|
753
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
754
|
+
(change: IStateChange) => {
|
|
755
|
+
console.log(
|
|
756
|
+
`${change.index}: ${(change.newValue as Date).toUTCString()}`,
|
|
757
|
+
);
|
|
758
|
+
},
|
|
759
|
+
);
|
|
760
|
+
try {
|
|
761
|
+
console.log('Initial value:');
|
|
762
|
+
stateManager.watchState(model, 'date', watchIndexRecursiveRule);
|
|
718
763
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
console.log('Set value:');
|
|
733
|
-
stateContext.date = new Date(2024, 5, 6);
|
|
734
|
-
|
|
735
|
-
console.log('Latest value:');
|
|
736
|
-
console.log(stateManager.getState<Date>(stateContext, 'date').toUTCString());
|
|
737
|
-
} finally {
|
|
738
|
-
changeSubscription.unsubscribe();
|
|
739
|
-
// Always release the state when it is no longer needed.
|
|
740
|
-
stateManager.releaseState(stateContext, 'date', truePredicate);
|
|
741
|
-
}
|
|
764
|
+
console.log('Changed value:');
|
|
765
|
+
model.date.setFullYear(2023);
|
|
766
|
+
|
|
767
|
+
console.log('Set value:');
|
|
768
|
+
model.date = new Date(2024, 5, 6);
|
|
769
|
+
|
|
770
|
+
console.log('Latest value:');
|
|
771
|
+
console.log(stateManager.getState<Date>(model, 'date').toUTCString());
|
|
772
|
+
} finally {
|
|
773
|
+
changeSubscription.unsubscribe();
|
|
774
|
+
// Always release the state when it is no longer needed.
|
|
775
|
+
stateManager.releaseState(model, 'date', watchIndexRecursiveRule);
|
|
776
|
+
}
|
|
742
777
|
}
|
|
743
778
|
|
|
744
779
|
function watchDateProperty(stateManager: IStateManager) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
780
|
+
console.log('\n******************************************');
|
|
781
|
+
console.log('* Watching year');
|
|
782
|
+
console.log('******************************************\n');
|
|
783
|
+
const date = utCDate(2021, 2, 5);
|
|
784
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
785
|
+
(change: IStateChange) => {
|
|
786
|
+
console.log(change.newValue);
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
try {
|
|
790
|
+
// This will emit a change event with the initial (current) value.
|
|
791
|
+
console.log('Initial value:');
|
|
792
|
+
stateManager.watchState(date, 'year');
|
|
793
|
+
|
|
794
|
+
const proxyRegister: IProxyRegistry = InjectionContainer.get(
|
|
795
|
+
RsXStateManagerInjectionTokens.IProxyRegistry,
|
|
796
|
+
);
|
|
797
|
+
const dateProxy = proxyRegister.getProxy<Date>(date);
|
|
798
|
+
console.log('Changed value:');
|
|
799
|
+
dateProxy.setFullYear(2023);
|
|
800
|
+
|
|
801
|
+
console.log('Latest value:');
|
|
802
|
+
console.log(stateManager.getState(date, 'year'));
|
|
803
|
+
} finally {
|
|
804
|
+
changeSubscription.unsubscribe();
|
|
805
|
+
stateManager.releaseState(date, 'year');
|
|
806
|
+
}
|
|
769
807
|
}
|
|
770
808
|
|
|
771
809
|
export const run = (() => {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
810
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
811
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
812
|
+
);
|
|
813
|
+
watchDate(stateManager);
|
|
814
|
+
watchDateProperty(stateManager);
|
|
777
815
|
})();
|
|
778
816
|
```
|
|
817
|
+
|
|
779
818
|
**Output:**
|
|
819
|
+
|
|
780
820
|
```console
|
|
781
821
|
Running demo: demo/src/rs-x-state-manager/register-date.ts
|
|
782
822
|
|
|
@@ -806,54 +846,60 @@ Latest value:
|
|
|
806
846
|
```
|
|
807
847
|
|
|
808
848
|
### Array
|
|
849
|
+
|
|
809
850
|
**Example**
|
|
851
|
+
|
|
810
852
|
```ts
|
|
811
|
-
import { InjectionContainer, printValue
|
|
853
|
+
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
812
854
|
import {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
855
|
+
type IStateChange,
|
|
856
|
+
type IStateManager,
|
|
857
|
+
RsXStateManagerInjectionTokens,
|
|
858
|
+
RsXStateManagerModule,
|
|
859
|
+
watchIndexRecursiveRule,
|
|
817
860
|
} from '@rs-x/state-manager';
|
|
818
861
|
|
|
819
862
|
// Load the state manager module into the injection container
|
|
820
863
|
InjectionContainer.load(RsXStateManagerModule);
|
|
821
864
|
|
|
822
865
|
export const run = (() => {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const changeSubscription = stateManager.changed.subscribe((change: IStateChange) => {
|
|
835
|
-
printValue(change.newValue);
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
try {
|
|
839
|
-
// This will emit a change event with the initial (current) value.
|
|
840
|
-
console.log('Initial value:');
|
|
841
|
-
stateManager.watchState(stateContext, 'array', truePredicate);
|
|
842
|
-
|
|
843
|
-
console.log('Changed value:');
|
|
844
|
-
stateContext.array[1].push(5);
|
|
866
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
867
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const model = {
|
|
871
|
+
array: [
|
|
872
|
+
[1, 2],
|
|
873
|
+
[3, 4],
|
|
874
|
+
],
|
|
875
|
+
};
|
|
845
876
|
|
|
846
|
-
|
|
847
|
-
|
|
877
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
878
|
+
(change: IStateChange) => {
|
|
879
|
+
printValue(change.newValue);
|
|
880
|
+
},
|
|
881
|
+
);
|
|
848
882
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
883
|
+
try {
|
|
884
|
+
// This will emit a change event with the initial (current) value.
|
|
885
|
+
console.log('Initial value:');
|
|
886
|
+
stateManager.watchState(model, 'array', watchIndexRecursiveRule);
|
|
887
|
+
|
|
888
|
+
console.log('Changed value:');
|
|
889
|
+
model.array[1].push(5);
|
|
890
|
+
|
|
891
|
+
console.log('Latest value:');
|
|
892
|
+
printValue(stateManager.getState(model, 'array'));
|
|
893
|
+
} finally {
|
|
894
|
+
changeSubscription.unsubscribe();
|
|
895
|
+
// Always release the state when it is no longer needed.
|
|
896
|
+
stateManager.releaseState(model, 'array', watchIndexRecursiveRule);
|
|
897
|
+
}
|
|
854
898
|
})();
|
|
855
899
|
```
|
|
900
|
+
|
|
856
901
|
**Output:**
|
|
902
|
+
|
|
857
903
|
```console
|
|
858
904
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-array.ts
|
|
859
905
|
Initial value:
|
|
@@ -894,54 +940,60 @@ Latest value:
|
|
|
894
940
|
```
|
|
895
941
|
|
|
896
942
|
### Map
|
|
943
|
+
|
|
897
944
|
**Example**
|
|
945
|
+
|
|
898
946
|
```ts
|
|
899
|
-
import { InjectionContainer, printValue
|
|
947
|
+
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
900
948
|
import {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
949
|
+
type IStateChange,
|
|
950
|
+
type IStateManager,
|
|
951
|
+
RsXStateManagerInjectionTokens,
|
|
952
|
+
RsXStateManagerModule,
|
|
953
|
+
watchIndexRecursiveRule,
|
|
905
954
|
} from '@rs-x/state-manager';
|
|
906
955
|
|
|
907
956
|
// Load the state manager module into the injection container
|
|
908
957
|
InjectionContainer.load(RsXStateManagerModule);
|
|
909
958
|
|
|
910
959
|
const stateManager: IStateManager = InjectionContainer.get(
|
|
911
|
-
|
|
960
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
912
961
|
);
|
|
913
962
|
|
|
914
963
|
export const run = (() => {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
964
|
+
const model = {
|
|
965
|
+
map: new Map([
|
|
966
|
+
['a', [1, 2]],
|
|
967
|
+
['b', [3, 4]],
|
|
968
|
+
]),
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
972
|
+
(change: IStateChange) => {
|
|
973
|
+
printValue(change.newValue);
|
|
974
|
+
},
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
try {
|
|
978
|
+
// This will emit a change event with the initial (current) value.
|
|
979
|
+
console.log('Initial value:');
|
|
980
|
+
stateManager.watchState(model, 'map', watchIndexRecursiveRule);
|
|
981
|
+
|
|
982
|
+
console.log('Changed value:');
|
|
983
|
+
model.map.get('b')?.push(5);
|
|
984
|
+
|
|
985
|
+
console.log('Latest value:');
|
|
986
|
+
printValue(stateManager.getState(model, 'map'));
|
|
987
|
+
} finally {
|
|
988
|
+
changeSubscription.unsubscribe();
|
|
989
|
+
// Always release the state when it is no longer needed.
|
|
990
|
+
stateManager.releaseState(model, 'array', watchIndexRecursiveRule);
|
|
991
|
+
}
|
|
942
992
|
})();
|
|
943
993
|
```
|
|
994
|
+
|
|
944
995
|
**Output:**
|
|
996
|
+
|
|
945
997
|
```console
|
|
946
998
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-map.ts
|
|
947
999
|
Initial value:
|
|
@@ -1000,53 +1052,61 @@ Latest value:
|
|
|
1000
1052
|
```
|
|
1001
1053
|
|
|
1002
1054
|
### Set
|
|
1055
|
+
|
|
1003
1056
|
**Example**
|
|
1057
|
+
|
|
1004
1058
|
```ts
|
|
1005
|
-
import { InjectionContainer, printValue
|
|
1059
|
+
import { InjectionContainer, printValue } from '@rs-x/core';
|
|
1006
1060
|
import {
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1061
|
+
type IProxyRegistry,
|
|
1062
|
+
type IStateChange,
|
|
1063
|
+
type IStateManager,
|
|
1064
|
+
RsXStateManagerInjectionTokens,
|
|
1065
|
+
RsXStateManagerModule,
|
|
1066
|
+
watchIndexRecursiveRule,
|
|
1012
1067
|
} from '@rs-x/state-manager';
|
|
1013
1068
|
|
|
1014
1069
|
// Load the state manager module into the injection container
|
|
1015
1070
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1016
1071
|
|
|
1017
1072
|
export const run = (() => {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const proxyRegister: IProxyRegistry = InjectionContainer.get(RsXStateManagerInjectionTokens.IProxyRegistry);
|
|
1037
|
-
proxyRegister.getProxy<number[]>(item2).push(5);
|
|
1038
|
-
|
|
1039
|
-
console.log('Latest value:');
|
|
1040
|
-
printValue(stateManager.getState(stateContext, 'set'));
|
|
1073
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1074
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1075
|
+
);
|
|
1076
|
+
const item1 = [1, 2];
|
|
1077
|
+
const item2 = [3, 4];
|
|
1078
|
+
const model = {
|
|
1079
|
+
set: new Set([item1, item2]),
|
|
1080
|
+
};
|
|
1081
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1082
|
+
(change: IStateChange) => {
|
|
1083
|
+
printValue(change.newValue);
|
|
1084
|
+
},
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
try {
|
|
1088
|
+
// This will emit a change event with the initial (current) value.
|
|
1089
|
+
console.log('Initial value:');
|
|
1090
|
+
stateManager.watchState(model, 'set', watchIndexRecursiveRule);
|
|
1041
1091
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1092
|
+
console.log('Changed value:');
|
|
1093
|
+
const proxyRegister: IProxyRegistry = InjectionContainer.get(
|
|
1094
|
+
RsXStateManagerInjectionTokens.IProxyRegistry,
|
|
1095
|
+
);
|
|
1096
|
+
proxyRegister.getProxy<number[]>(item2).push(5);
|
|
1097
|
+
|
|
1098
|
+
console.log('Latest value:');
|
|
1099
|
+
printValue(stateManager.getState(model, 'set'));
|
|
1100
|
+
} finally {
|
|
1101
|
+
changeSubscription.unsubscribe();
|
|
1102
|
+
// Always release the state when it is no longer needed.
|
|
1103
|
+
stateManager.releaseState(model, 'set', watchIndexRecursiveRule);
|
|
1104
|
+
}
|
|
1047
1105
|
})();
|
|
1048
1106
|
```
|
|
1107
|
+
|
|
1049
1108
|
**Output:**
|
|
1109
|
+
|
|
1050
1110
|
```console
|
|
1051
1111
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-set.ts
|
|
1052
1112
|
Initial value:
|
|
@@ -1087,54 +1147,64 @@ Latest value:
|
|
|
1087
1147
|
```
|
|
1088
1148
|
|
|
1089
1149
|
### Promise
|
|
1150
|
+
|
|
1090
1151
|
**Example**
|
|
1152
|
+
|
|
1091
1153
|
```ts
|
|
1092
1154
|
import { InjectionContainer, WaitForEvent } from '@rs-x/core';
|
|
1093
1155
|
import {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1156
|
+
type IStateChange,
|
|
1157
|
+
type IStateManager,
|
|
1158
|
+
RsXStateManagerInjectionTokens,
|
|
1159
|
+
RsXStateManagerModule,
|
|
1098
1160
|
} from '@rs-x/state-manager';
|
|
1099
1161
|
|
|
1100
1162
|
// Load the state manager module into the injection container
|
|
1101
1163
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1102
1164
|
|
|
1103
1165
|
export const run = (async () => {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1166
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1167
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1168
|
+
);
|
|
1169
|
+
|
|
1170
|
+
const model = {
|
|
1171
|
+
promise: Promise.resolve(10),
|
|
1172
|
+
};
|
|
1173
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1174
|
+
(change: IStateChange) => {
|
|
1175
|
+
console.log(change.newValue);
|
|
1176
|
+
},
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
try {
|
|
1180
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1181
|
+
// This will emit a change event with the initial (current) value.
|
|
1182
|
+
console.log('Initial value:');
|
|
1183
|
+
stateManager.watchState(model, 'promise');
|
|
1113
1184
|
});
|
|
1114
1185
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
console.log('Initial value:');
|
|
1119
|
-
stateManager.watchState(stateContext, 'promise');
|
|
1120
|
-
});
|
|
1186
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1187
|
+
console.log('Changed value:');
|
|
1188
|
+
let resolveHandler!: (value: number) => void;
|
|
1121
1189
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
stateContext.promise = new Promise((resolve) => { resolveHandler = resolve; });
|
|
1126
|
-
resolveHandler(30);
|
|
1127
|
-
});
|
|
1190
|
+
model.promise = new Promise<number>((resolve) => {
|
|
1191
|
+
resolveHandler = resolve;
|
|
1192
|
+
});
|
|
1128
1193
|
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1194
|
+
resolveHandler(30);
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
console.log(`Latest value: ${stateManager.getState(model, 'promise')}`);
|
|
1198
|
+
} finally {
|
|
1199
|
+
changeSubscription.unsubscribe();
|
|
1200
|
+
// Always release the state when it is no longer needed.
|
|
1201
|
+
stateManager.releaseState(model, 'promise');
|
|
1202
|
+
}
|
|
1135
1203
|
})();
|
|
1136
1204
|
```
|
|
1205
|
+
|
|
1137
1206
|
**Output:**
|
|
1207
|
+
|
|
1138
1208
|
```console
|
|
1139
1209
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-promise.ts
|
|
1140
1210
|
Initial value:
|
|
@@ -1145,59 +1215,65 @@ Latest value: 30
|
|
|
1145
1215
|
```
|
|
1146
1216
|
|
|
1147
1217
|
### Observable
|
|
1218
|
+
|
|
1148
1219
|
**Example**
|
|
1220
|
+
|
|
1149
1221
|
```ts
|
|
1222
|
+
import { of, Subject } from 'rxjs';
|
|
1223
|
+
|
|
1150
1224
|
import { InjectionContainer, WaitForEvent } from '@rs-x/core';
|
|
1151
1225
|
import {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1226
|
+
type IStateChange,
|
|
1227
|
+
type IStateManager,
|
|
1228
|
+
RsXStateManagerInjectionTokens,
|
|
1229
|
+
RsXStateManagerModule,
|
|
1156
1230
|
} from '@rs-x/state-manager';
|
|
1157
|
-
import { of, Subject } from 'rxjs';
|
|
1158
1231
|
|
|
1159
1232
|
// Load the state manager module into the injection container
|
|
1160
1233
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1161
1234
|
|
|
1162
1235
|
export const run = (async () => {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1236
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1237
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1238
|
+
);
|
|
1239
|
+
|
|
1240
|
+
const model = {
|
|
1241
|
+
observable: of(10),
|
|
1242
|
+
};
|
|
1243
|
+
|
|
1244
|
+
const changeSubscription = stateManager.changed.subscribe(
|
|
1245
|
+
(change: IStateChange) => {
|
|
1246
|
+
console.log(change.newValue);
|
|
1247
|
+
},
|
|
1248
|
+
);
|
|
1249
|
+
|
|
1250
|
+
try {
|
|
1251
|
+
// We need to wait here until the event is emitted,
|
|
1252
|
+
// otherwise the demo will exit before the change event occurs.
|
|
1253
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1254
|
+
// This will emit a change event with the initial (current) value.
|
|
1255
|
+
console.log('Initial value:');
|
|
1256
|
+
stateManager.watchState(model, 'observable');
|
|
1173
1257
|
});
|
|
1174
1258
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
stateManager.watchState(stateContext, 'observable');
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1185
|
-
console.log('Changed value:');
|
|
1186
|
-
const subject = new Subject<number>();
|
|
1187
|
-
stateContext.observable = subject
|
|
1188
|
-
subject.next(30);
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
console.log(`Latest value: ${stateManager.getState(stateContext, 'observable')}`);
|
|
1259
|
+
await new WaitForEvent(stateManager, 'changed').wait(() => {
|
|
1260
|
+
console.log('Changed value:');
|
|
1261
|
+
const subject = new Subject<number>();
|
|
1262
|
+
model.observable = subject;
|
|
1263
|
+
subject.next(30);
|
|
1264
|
+
});
|
|
1192
1265
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1266
|
+
console.log(`Latest value: ${stateManager.getState(model, 'observable')}`);
|
|
1267
|
+
} finally {
|
|
1268
|
+
changeSubscription.unsubscribe();
|
|
1269
|
+
// Always release the state when it is no longer needed.
|
|
1270
|
+
stateManager.releaseState(model, 'observable');
|
|
1271
|
+
}
|
|
1198
1272
|
})();
|
|
1199
1273
|
```
|
|
1274
|
+
|
|
1200
1275
|
**Output:**
|
|
1276
|
+
|
|
1201
1277
|
```console
|
|
1202
1278
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/register-observable.ts
|
|
1203
1279
|
Initial value:
|
|
@@ -1208,511 +1284,601 @@ Latest value: 30
|
|
|
1208
1284
|
```
|
|
1209
1285
|
|
|
1210
1286
|
### Custom type
|
|
1211
|
-
|
|
1212
|
-
|
|
1287
|
+
|
|
1288
|
+
1. Create an accessor to retrieve index values on your type.
|
|
1289
|
+
2. Create a factory to create an observer for your data type.
|
|
1213
1290
|
3. Create a factory to create an observer for an index on your data instance.
|
|
1214
1291
|
|
|
1215
1292
|
The following example demonstrates adding support for a custom `TextDocument` class:
|
|
1216
1293
|
|
|
1217
1294
|
**Example**
|
|
1218
|
-
|
|
1295
|
+
|
|
1296
|
+
````ts
|
|
1297
|
+
import { ReplaySubject, Subscription } from 'rxjs';
|
|
1298
|
+
|
|
1219
1299
|
import {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1300
|
+
ContainerModule,
|
|
1301
|
+
defaultIndexValueAccessorList,
|
|
1302
|
+
type IDisposableOwner,
|
|
1303
|
+
type IErrorLog,
|
|
1304
|
+
type IGuidFactory,
|
|
1305
|
+
type IIndexValueAccessor,
|
|
1306
|
+
Inject,
|
|
1307
|
+
Injectable,
|
|
1308
|
+
InjectionContainer,
|
|
1309
|
+
type IPropertyChange,
|
|
1310
|
+
type IValueMetadata,
|
|
1311
|
+
overrideMultiInjectServices,
|
|
1312
|
+
RsXCoreInjectionTokens,
|
|
1313
|
+
SingletonFactory,
|
|
1314
|
+
Type,
|
|
1234
1315
|
} from '@rs-x/core';
|
|
1235
1316
|
import {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1317
|
+
AbstractObserver,
|
|
1318
|
+
defaultObjectObserverProxyPairFactoryList,
|
|
1319
|
+
defaultPropertyObserverProxyPairFactoryList,
|
|
1320
|
+
type IIndexObserverInfo,
|
|
1321
|
+
IndexObserverProxyPairFactory,
|
|
1322
|
+
type IObjectObserverProxyPairFactory,
|
|
1323
|
+
type IObjectObserverProxyPairManager,
|
|
1324
|
+
type IObserverProxyPair,
|
|
1325
|
+
type IPropertyInfo,
|
|
1326
|
+
type IProxyRegistry,
|
|
1327
|
+
type IProxyTarget,
|
|
1328
|
+
type IStateChange,
|
|
1329
|
+
type IStateManager,
|
|
1330
|
+
RsXStateManagerInjectionTokens,
|
|
1331
|
+
RsXStateManagerModule,
|
|
1332
|
+
watchIndexRecursiveRule,
|
|
1251
1333
|
} from '@rs-x/state-manager';
|
|
1252
|
-
import { ReplaySubject, Subscription } from 'rxjs';
|
|
1253
1334
|
|
|
1254
1335
|
const MyInjectTokens = {
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1336
|
+
TextDocumentObserverManager: Symbol('TextDocumentObserverManager'),
|
|
1337
|
+
TextDocumenIndexObserverManager: Symbol('TextDocumenIndexObserverManager'),
|
|
1338
|
+
TextDocumentIndexAccessor: Symbol('TextDocumentIndexAccessor'),
|
|
1339
|
+
TextDocumentObserverProxyPairFactory: Symbol(
|
|
1340
|
+
'TextDocumentObserverProxyPairFactory',
|
|
1341
|
+
),
|
|
1342
|
+
TextDocumentInxdexObserverProxyPairFactory: Symbol(
|
|
1343
|
+
'TextDocumentInxdexObserverProxyPairFactory',
|
|
1344
|
+
),
|
|
1261
1345
|
};
|
|
1262
1346
|
|
|
1263
|
-
class IndexForTextDocumentxObserverManager
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
)
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1347
|
+
class IndexForTextDocumentxObserverManager extends SingletonFactory<
|
|
1348
|
+
number,
|
|
1349
|
+
IIndexObserverInfo<ITextDocumentIndex>,
|
|
1350
|
+
TextDocumentIndexObserver
|
|
1351
|
+
> {
|
|
1352
|
+
constructor(
|
|
1353
|
+
private readonly _textDocument: TextDocument,
|
|
1354
|
+
private readonly _textDocumentObserverManager: TextDocumentObserverManager,
|
|
1355
|
+
private readonly releaseOwner: () => void,
|
|
1356
|
+
) {
|
|
1357
|
+
super();
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
public override getId(
|
|
1361
|
+
indexObserverInfo: IIndexObserverInfo<ITextDocumentIndex>,
|
|
1362
|
+
): number {
|
|
1363
|
+
return this.createId(indexObserverInfo);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
protected override createInstance(
|
|
1367
|
+
indexObserverInfo: IIndexObserverInfo<ITextDocumentIndex>,
|
|
1368
|
+
id: number,
|
|
1369
|
+
): TextDocumentIndexObserver {
|
|
1370
|
+
const textDocumentObserver = this._textDocumentObserverManager.create(
|
|
1371
|
+
this._textDocument,
|
|
1372
|
+
).instance;
|
|
1373
|
+
return new TextDocumentIndexObserver(
|
|
1374
|
+
{
|
|
1375
|
+
canDispose: () => this.getReferenceCount(id) === 1,
|
|
1376
|
+
release: () => {
|
|
1377
|
+
textDocumentObserver.dispose();
|
|
1378
|
+
this.release(id);
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
textDocumentObserver,
|
|
1382
|
+
indexObserverInfo.index,
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
protected override createId(
|
|
1387
|
+
indexObserverInfo: IIndexObserverInfo<ITextDocumentIndex>,
|
|
1388
|
+
): number {
|
|
1389
|
+
// Using Cantor pairing to create a unique id from page and line index
|
|
1390
|
+
const { pageIndex, lineIndex } = indexObserverInfo.index;
|
|
1391
|
+
return (
|
|
1392
|
+
((pageIndex + lineIndex) * (pageIndex + lineIndex + 1)) / 2 + lineIndex
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1299
1395
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
}
|
|
1396
|
+
protected override onReleased(): void {
|
|
1397
|
+
if (this.isEmpty) {
|
|
1398
|
+
this.releaseOwner();
|
|
1304
1399
|
}
|
|
1400
|
+
}
|
|
1305
1401
|
}
|
|
1306
1402
|
|
|
1307
1403
|
// We want to ensure that for the same TextDocument we always have the same observer
|
|
1308
1404
|
@Injectable()
|
|
1309
|
-
class TextDocumentObserverManager extends SingletonFactory<
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1405
|
+
class TextDocumentObserverManager extends SingletonFactory<
|
|
1406
|
+
TextDocument,
|
|
1407
|
+
TextDocument,
|
|
1408
|
+
TextDocumentObserver
|
|
1409
|
+
> {
|
|
1410
|
+
constructor(
|
|
1411
|
+
@Inject(RsXStateManagerInjectionTokens.IProxyRegistry)
|
|
1412
|
+
private readonly _proxyRegister: IProxyRegistry,
|
|
1413
|
+
) {
|
|
1414
|
+
super();
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
public override getId(textDocument: TextDocument): TextDocument {
|
|
1418
|
+
return textDocument;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
protected override createInstance(
|
|
1422
|
+
textDocument: TextDocument,
|
|
1423
|
+
id: TextDocument,
|
|
1424
|
+
): TextDocumentObserver {
|
|
1425
|
+
return new TextDocumentObserver(textDocument, this._proxyRegister, {
|
|
1426
|
+
canDispose: () => this.getReferenceCount(id) === 1,
|
|
1427
|
+
release: () => this.release(id),
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1330
1430
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1431
|
+
protected override createId(textDocument: TextDocument): TextDocument {
|
|
1432
|
+
return textDocument;
|
|
1433
|
+
}
|
|
1334
1434
|
}
|
|
1335
1435
|
|
|
1336
1436
|
// We want to ensure we create only one index-manager per TextDocument
|
|
1337
1437
|
@Injectable()
|
|
1338
|
-
export class TextDocumenIndexObserverManager
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
)
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1438
|
+
export class TextDocumenIndexObserverManager extends SingletonFactory<
|
|
1439
|
+
TextDocument,
|
|
1440
|
+
TextDocument,
|
|
1441
|
+
IndexForTextDocumentxObserverManager
|
|
1442
|
+
> {
|
|
1443
|
+
constructor(
|
|
1444
|
+
@Inject(MyInjectTokens.TextDocumentObserverManager)
|
|
1445
|
+
private readonly _textDocumentObserverManager: TextDocumentObserverManager,
|
|
1446
|
+
) {
|
|
1447
|
+
super();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
public override getId(textDocument: TextDocument): TextDocument {
|
|
1451
|
+
return textDocument;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
protected override createId(textDocument: TextDocument): TextDocument {
|
|
1455
|
+
return textDocument;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
protected override createInstance(
|
|
1459
|
+
textDocument: TextDocument,
|
|
1460
|
+
): IndexForTextDocumentxObserverManager {
|
|
1461
|
+
return new IndexForTextDocumentxObserverManager(
|
|
1462
|
+
textDocument,
|
|
1463
|
+
this._textDocumentObserverManager,
|
|
1464
|
+
() => this.release(textDocument),
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1365
1467
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1468
|
+
protected override releaseInstance(
|
|
1469
|
+
indexForTextDocumentxObserverManager: IndexForTextDocumentxObserverManager,
|
|
1470
|
+
): void {
|
|
1471
|
+
indexForTextDocumentxObserverManager.dispose();
|
|
1472
|
+
}
|
|
1371
1473
|
}
|
|
1372
1474
|
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
1475
|
@Injectable()
|
|
1377
|
-
export class TextDocumentIndexAccessor implements IIndexValueAccessor<
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1476
|
+
export class TextDocumentIndexAccessor implements IIndexValueAccessor<
|
|
1477
|
+
TextDocument,
|
|
1478
|
+
ITextDocumentIndex
|
|
1479
|
+
> {
|
|
1480
|
+
public readonly priority!: 200;
|
|
1481
|
+
|
|
1482
|
+
public hasValue(
|
|
1483
|
+
textDocument: TextDocument,
|
|
1484
|
+
index: ITextDocumentIndex,
|
|
1485
|
+
): boolean {
|
|
1486
|
+
return textDocument.getLine(index) !== undefined;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// We don’t have any properties that can be iterated through.
|
|
1490
|
+
public getIndexes(): IterableIterator<ITextDocumentIndex> {
|
|
1491
|
+
return [].values();
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// Here it is the same as getValue.
|
|
1495
|
+
// For example, for a Promise accessor getValue returns the promise
|
|
1496
|
+
// and getResolvedValue returns the resolved promise value
|
|
1497
|
+
public getResolvedValue(
|
|
1498
|
+
textDocument: TextDocument,
|
|
1499
|
+
index: ITextDocumentIndex,
|
|
1500
|
+
): string | undefined {
|
|
1501
|
+
return this.getValue(textDocument, index);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
public getValue(
|
|
1505
|
+
textDocument: TextDocument,
|
|
1506
|
+
index: ITextDocumentIndex,
|
|
1507
|
+
): string | undefined {
|
|
1508
|
+
return textDocument.getLine(index);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
public setValue(
|
|
1512
|
+
textDocument: TextDocument,
|
|
1513
|
+
index: ITextDocumentIndex,
|
|
1514
|
+
value: string,
|
|
1515
|
+
): void {
|
|
1516
|
+
textDocument.setLine(index, value);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
public applies(textDocument: unknown, _index: ITextDocumentIndex): boolean {
|
|
1520
|
+
return textDocument instanceof TextDocument;
|
|
1521
|
+
}
|
|
1412
1522
|
}
|
|
1413
1523
|
|
|
1414
1524
|
@Injectable()
|
|
1415
|
-
export class TextDocumentInxdexObserverProxyPairFactory extends IndexObserverProxyPairFactory<
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1525
|
+
export class TextDocumentInxdexObserverProxyPairFactory extends IndexObserverProxyPairFactory<
|
|
1526
|
+
TextDocument,
|
|
1527
|
+
unknown
|
|
1528
|
+
> {
|
|
1529
|
+
constructor(
|
|
1530
|
+
@Inject(RsXStateManagerInjectionTokens.IObjectObserverProxyPairManager)
|
|
1531
|
+
objectObserverManager: IObjectObserverProxyPairManager,
|
|
1532
|
+
@Inject(MyInjectTokens.TextDocumenIndexObserverManager)
|
|
1533
|
+
textDocumenIndexObserverManager: TextDocumenIndexObserverManager,
|
|
1534
|
+
@Inject(RsXCoreInjectionTokens.IErrorLog)
|
|
1535
|
+
errorLog: IErrorLog,
|
|
1536
|
+
@Inject(RsXCoreInjectionTokens.IGuidFactory)
|
|
1537
|
+
guidFactory: IGuidFactory,
|
|
1538
|
+
@Inject(RsXCoreInjectionTokens.IIndexValueAccessor)
|
|
1539
|
+
indexValueAccessor: IIndexValueAccessor,
|
|
1540
|
+
@Inject(RsXStateManagerInjectionTokens.IProxyRegistry)
|
|
1541
|
+
proxyRegister: IProxyRegistry,
|
|
1542
|
+
@Inject(RsXCoreInjectionTokens.IValueMetadata)
|
|
1543
|
+
valueMetadata: IValueMetadata,
|
|
1544
|
+
) {
|
|
1545
|
+
super(
|
|
1546
|
+
objectObserverManager,
|
|
1547
|
+
Type.cast(textDocumenIndexObserverManager),
|
|
1548
|
+
errorLog,
|
|
1549
|
+
guidFactory,
|
|
1550
|
+
indexValueAccessor,
|
|
1551
|
+
proxyRegister,
|
|
1552
|
+
valueMetadata,
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
public applies(object: unknown, propertyInfo: IPropertyInfo): boolean {
|
|
1557
|
+
const documentKey = propertyInfo.index as ITextDocumentIndex;
|
|
1558
|
+
return (
|
|
1559
|
+
object instanceof TextDocument &&
|
|
1560
|
+
documentKey?.lineIndex >= 0 &&
|
|
1561
|
+
documentKey?.pageIndex >= 0
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1444
1564
|
}
|
|
1445
1565
|
|
|
1446
1566
|
@Injectable()
|
|
1447
1567
|
export class TextDocumentObserverProxyPairFactory implements IObjectObserverProxyPairFactory {
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1568
|
+
public readonly priority = 100;
|
|
1569
|
+
|
|
1570
|
+
constructor(
|
|
1571
|
+
@Inject(MyInjectTokens.TextDocumentObserverManager)
|
|
1572
|
+
private readonly _textDocumentObserverManager: TextDocumentObserverManager,
|
|
1573
|
+
) {}
|
|
1574
|
+
|
|
1575
|
+
public create(
|
|
1576
|
+
_: IDisposableOwner,
|
|
1577
|
+
proxyTarget: IProxyTarget<TextDocument>,
|
|
1578
|
+
): IObserverProxyPair<TextDocument> {
|
|
1579
|
+
const observer = this._textDocumentObserverManager.create(
|
|
1580
|
+
proxyTarget.target,
|
|
1581
|
+
).instance;
|
|
1582
|
+
return {
|
|
1583
|
+
observer,
|
|
1584
|
+
proxy: observer.target as TextDocument,
|
|
1585
|
+
proxyTarget: proxyTarget.target,
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1465
1588
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1589
|
+
public applies(object: unknown): boolean {
|
|
1590
|
+
return object instanceof TextDocument;
|
|
1591
|
+
}
|
|
1469
1592
|
}
|
|
1470
1593
|
|
|
1471
1594
|
interface ITextDocumentIndex {
|
|
1472
|
-
|
|
1473
|
-
|
|
1595
|
+
pageIndex: number;
|
|
1596
|
+
lineIndex: number;
|
|
1474
1597
|
}
|
|
1475
1598
|
|
|
1476
1599
|
class TextDocument {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
});
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
1600
|
+
private readonly _pages = new Map<number, Map<number, string>>();
|
|
1601
|
+
constructor(pages?: string[][]) {
|
|
1602
|
+
pages?.forEach((page, pageIndex) => {
|
|
1603
|
+
const pageText = new Map<number, string>();
|
|
1604
|
+
|
|
1605
|
+
this._pages.set(pageIndex, pageText);
|
|
1606
|
+
page.forEach((lineText, lineIndex) => {
|
|
1607
|
+
pageText.set(lineIndex, lineText);
|
|
1608
|
+
});
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1491
1611
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1612
|
+
public toString(): string {
|
|
1613
|
+
const pages: string[] = [];
|
|
1494
1614
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1615
|
+
// Sort pages by pageIndex
|
|
1616
|
+
const sortedPageIndexes = Array.from(this._pages.keys()).sort(
|
|
1617
|
+
(a, b) => a - b,
|
|
1618
|
+
);
|
|
1497
1619
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1620
|
+
for (const pageIndex of sortedPageIndexes) {
|
|
1621
|
+
const page = this._pages.get(pageIndex);
|
|
1622
|
+
if (!page) {
|
|
1623
|
+
continue;
|
|
1624
|
+
}
|
|
1503
1625
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1626
|
+
// Sort lines by lineIndex
|
|
1627
|
+
const sortedLineIndexes = Array.from(page.keys()).sort((a, b) => a - b);
|
|
1506
1628
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
return pages.join('\n\n');
|
|
1629
|
+
const lines = sortedLineIndexes.map(
|
|
1630
|
+
(lineIndex) => ` ${lineIndex}: ${page.get(lineIndex)}`,
|
|
1631
|
+
);
|
|
1632
|
+
pages.push(`Page ${pageIndex}:\n${lines.join('\n')}`);
|
|
1512
1633
|
}
|
|
1513
1634
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
let page = this._pages.get(pageIndex);
|
|
1517
|
-
if (!page) {
|
|
1518
|
-
page = new Map();
|
|
1519
|
-
this._pages.set(pageIndex, page);
|
|
1520
|
-
}
|
|
1635
|
+
return pages.join('\n\n');
|
|
1636
|
+
}
|
|
1521
1637
|
|
|
1522
|
-
|
|
1638
|
+
public setLine(index: ITextDocumentIndex, text: string): void {
|
|
1639
|
+
const { pageIndex, lineIndex } = index;
|
|
1640
|
+
let page = this._pages.get(pageIndex);
|
|
1641
|
+
if (!page) {
|
|
1642
|
+
page = new Map();
|
|
1643
|
+
this._pages.set(pageIndex, page);
|
|
1523
1644
|
}
|
|
1524
1645
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
return this._pages.get(pageIndex)?.get(lineIndex);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1646
|
+
page.set(lineIndex, text);
|
|
1647
|
+
}
|
|
1530
1648
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1649
|
+
public getLine(index: ITextDocumentIndex): string | undefined {
|
|
1650
|
+
const { pageIndex, lineIndex } = index;
|
|
1651
|
+
return this._pages.get(pageIndex)?.get(lineIndex);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1533
1654
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1655
|
+
class TextDocumentIndexObserver extends AbstractObserver<
|
|
1656
|
+
TextDocument,
|
|
1657
|
+
string,
|
|
1658
|
+
ITextDocumentIndex
|
|
1659
|
+
> {
|
|
1660
|
+
private readonly _changeSubscription: Subscription;
|
|
1661
|
+
|
|
1662
|
+
constructor(
|
|
1663
|
+
owner: IDisposableOwner,
|
|
1664
|
+
private readonly _observer: TextDocumentObserver,
|
|
1665
|
+
index: ITextDocumentIndex,
|
|
1666
|
+
) {
|
|
1667
|
+
super(
|
|
1668
|
+
owner,
|
|
1669
|
+
_observer.target,
|
|
1670
|
+
_observer.target.getLine(index),
|
|
1671
|
+
new ReplaySubject(),
|
|
1672
|
+
index,
|
|
1673
|
+
);
|
|
1674
|
+
this._changeSubscription = _observer.changed.subscribe(this.onChange);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
protected override disposeInternal(): void {
|
|
1678
|
+
this._changeSubscription.unsubscribe();
|
|
1679
|
+
this._observer.dispose();
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
private readonly onChange = (change: IPropertyChange) => {
|
|
1683
|
+
const changeIndex = change.index as ITextDocumentIndex;
|
|
1684
|
+
if (
|
|
1685
|
+
changeIndex.lineIndex === this.id?.lineIndex &&
|
|
1686
|
+
changeIndex.pageIndex === this.id?.pageIndex
|
|
1538
1687
|
) {
|
|
1539
|
-
|
|
1540
|
-
this._changeSubscription = _observer.changed.subscribe(this.onChange);
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
protected override disposeInternal(): void {
|
|
1544
|
-
this._changeSubscription.unsubscribe();
|
|
1545
|
-
this._observer.dispose();
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
private readonly onChange = (change: IPropertyChange) => {
|
|
1549
|
-
const changeIndex = change.id as ITextDocumentIndex;
|
|
1550
|
-
if (changeIndex.lineIndex === this.id.lineIndex && changeIndex.pageIndex === this.id.pageIndex) {
|
|
1551
|
-
this.emitChange(change);
|
|
1552
|
-
}
|
|
1688
|
+
this.emitChange(change);
|
|
1553
1689
|
}
|
|
1690
|
+
};
|
|
1554
1691
|
}
|
|
1555
1692
|
|
|
1556
1693
|
class TextDocumentObserver extends AbstractObserver<TextDocument> {
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
}
|
|
1694
|
+
constructor(
|
|
1695
|
+
textDocument: TextDocument,
|
|
1696
|
+
private readonly _proxyRegister: IProxyRegistry,
|
|
1697
|
+
owner?: IDisposableOwner,
|
|
1698
|
+
) {
|
|
1699
|
+
super(owner, Type.cast(undefined), textDocument);
|
|
1700
|
+
|
|
1701
|
+
this.target = new Proxy(textDocument, this);
|
|
1702
|
+
|
|
1703
|
+
// Always register a proxy at the proxy registry
|
|
1704
|
+
// so we can determine if an instance is a proxy or not.
|
|
1705
|
+
this._proxyRegister.register(textDocument, this.target);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
protected override disposeInternal(): void {
|
|
1709
|
+
this._proxyRegister.unregister(this.value);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
public get(
|
|
1713
|
+
textDocument: TextDocument,
|
|
1714
|
+
property: PropertyKey,
|
|
1715
|
+
receiver: unknown,
|
|
1716
|
+
): unknown {
|
|
1717
|
+
if (property == 'setLine') {
|
|
1718
|
+
return (index: ITextDocumentIndex, text: string) => {
|
|
1719
|
+
textDocument.setLine(index, text);
|
|
1720
|
+
this.emitChange({
|
|
1721
|
+
arguments: [],
|
|
1722
|
+
index: index,
|
|
1723
|
+
target: textDocument,
|
|
1724
|
+
newValue: text,
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1727
|
+
} else {
|
|
1728
|
+
return Reflect.get(textDocument, property, receiver);
|
|
1593
1729
|
}
|
|
1730
|
+
}
|
|
1594
1731
|
}
|
|
1595
1732
|
|
|
1596
1733
|
// Load the state manager module into the injection container
|
|
1597
1734
|
InjectionContainer.load(RsXStateManagerModule);
|
|
1598
1735
|
|
|
1599
1736
|
const MyModule = new ContainerModule((options) => {
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
]
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1737
|
+
options
|
|
1738
|
+
.bind<TextDocumentObserverManager>(
|
|
1739
|
+
MyInjectTokens.TextDocumentObserverManager,
|
|
1740
|
+
)
|
|
1741
|
+
.to(TextDocumentObserverManager)
|
|
1742
|
+
.inSingletonScope();
|
|
1743
|
+
|
|
1744
|
+
options
|
|
1745
|
+
.bind<TextDocumenIndexObserverManager>(
|
|
1746
|
+
MyInjectTokens.TextDocumenIndexObserverManager,
|
|
1747
|
+
)
|
|
1748
|
+
.to(TextDocumenIndexObserverManager)
|
|
1749
|
+
.inSingletonScope();
|
|
1750
|
+
|
|
1751
|
+
overrideMultiInjectServices(
|
|
1752
|
+
options,
|
|
1753
|
+
RsXCoreInjectionTokens.IIndexValueAccessorList,
|
|
1754
|
+
[
|
|
1755
|
+
{
|
|
1756
|
+
target: TextDocumentIndexAccessor,
|
|
1757
|
+
token: MyInjectTokens.TextDocumentIndexAccessor,
|
|
1758
|
+
},
|
|
1759
|
+
...defaultIndexValueAccessorList,
|
|
1760
|
+
],
|
|
1761
|
+
);
|
|
1762
|
+
|
|
1763
|
+
overrideMultiInjectServices(
|
|
1764
|
+
options,
|
|
1765
|
+
RsXStateManagerInjectionTokens.IObjectObserverProxyPairFactoryList,
|
|
1766
|
+
[
|
|
1767
|
+
{
|
|
1768
|
+
target: TextDocumentObserverProxyPairFactory,
|
|
1769
|
+
token: MyInjectTokens.TextDocumentObserverProxyPairFactory,
|
|
1770
|
+
},
|
|
1771
|
+
...defaultObjectObserverProxyPairFactoryList,
|
|
1772
|
+
],
|
|
1773
|
+
);
|
|
1774
|
+
|
|
1775
|
+
overrideMultiInjectServices(
|
|
1776
|
+
options,
|
|
1777
|
+
RsXStateManagerInjectionTokens.IPropertyObserverProxyPairFactoryList,
|
|
1778
|
+
[
|
|
1779
|
+
{
|
|
1780
|
+
target: TextDocumentInxdexObserverProxyPairFactory,
|
|
1781
|
+
token: MyInjectTokens.TextDocumentInxdexObserverProxyPairFactory,
|
|
1782
|
+
},
|
|
1783
|
+
...defaultPropertyObserverProxyPairFactoryList,
|
|
1784
|
+
],
|
|
1785
|
+
);
|
|
1629
1786
|
});
|
|
1630
1787
|
|
|
1631
1788
|
InjectionContainer.load(MyModule);
|
|
1632
1789
|
|
|
1633
|
-
function testMonitorTextDocument(
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1790
|
+
function testMonitorTextDocument(
|
|
1791
|
+
stateManager: IStateManager,
|
|
1792
|
+
model: { myBook: TextDocument },
|
|
1793
|
+
): void {
|
|
1794
|
+
const bookSubscription = stateManager.changed.subscribe(() => {
|
|
1795
|
+
console.log(model.myBook.toString());
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
// We observe the whole book
|
|
1799
|
+
// This will use TextDocumentObserverProxyPairFactory
|
|
1800
|
+
try {
|
|
1801
|
+
console.log('\n***********************************************');
|
|
1802
|
+
console.log('Start watching the whole book\n');
|
|
1803
|
+
console.log('My initial book:\n');
|
|
1804
|
+
stateManager.watchState(model, 'myBook', watchIndexRecursiveRule);
|
|
1805
|
+
|
|
1806
|
+
console.log('\nUpdate second line on the first page:\n');
|
|
1807
|
+
console.log('My book after change:\n');
|
|
1808
|
+
model.myBook.setLine(
|
|
1809
|
+
{ pageIndex: 0, lineIndex: 1 },
|
|
1810
|
+
'In a far far away land',
|
|
1811
|
+
);
|
|
1812
|
+
} finally {
|
|
1813
|
+
// Stop monitoring the whole book
|
|
1814
|
+
stateManager.releaseState(model, 'myBook', watchIndexRecursiveRule);
|
|
1815
|
+
bookSubscription.unsubscribe();
|
|
1816
|
+
}
|
|
1655
1817
|
}
|
|
1656
1818
|
|
|
1657
|
-
function testMonitoreSpecificLineInDocument(
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1819
|
+
function testMonitoreSpecificLineInDocument(
|
|
1820
|
+
stateManager: IStateManager,
|
|
1821
|
+
model: { myBook: TextDocument },
|
|
1822
|
+
): void {
|
|
1823
|
+
const line3OnPage1Index = { pageIndex: 0, lineIndex: 2 };
|
|
1824
|
+
const lineSubscription = stateManager.changed.subscribe(
|
|
1825
|
+
(change: IStateChange) => {
|
|
1826
|
+
const documentIndex = change.index as ITextDocumentIndex;
|
|
1827
|
+
console.log(
|
|
1828
|
+
`Line ${documentIndex.lineIndex + 1} on page ${documentIndex.pageIndex + 1} has changed to '${change.newValue}'`,
|
|
1829
|
+
);
|
|
1830
|
+
console.log('My book after change:\n');
|
|
1831
|
+
console.log(model.myBook.toString());
|
|
1832
|
+
},
|
|
1833
|
+
);
|
|
1834
|
+
|
|
1835
|
+
try {
|
|
1836
|
+
// Here we only watch line 3 on page 1.
|
|
1837
|
+
// Notice that the line does not have to exist yet.
|
|
1838
|
+
// The initial book does not have a line 3 on page 1.
|
|
1839
|
+
//
|
|
1840
|
+
// TextDocumentInxdexObserverProxyPairFactory is used here
|
|
1841
|
+
|
|
1842
|
+
console.log('\n***********************************************');
|
|
1843
|
+
console.log('Start watching line 3 on page 1\n');
|
|
1844
|
+
stateManager.watchState(model.myBook, line3OnPage1Index);
|
|
1845
|
+
|
|
1846
|
+
const proxRegistry: IProxyRegistry = InjectionContainer.get(
|
|
1847
|
+
RsXStateManagerInjectionTokens.IProxyRegistry,
|
|
1848
|
+
);
|
|
1849
|
+
const bookProxy: TextDocument = proxRegistry.getProxy(model.myBook);
|
|
1850
|
+
|
|
1851
|
+
bookProxy.setLine(line3OnPage1Index, 'a prince was born');
|
|
1852
|
+
|
|
1853
|
+
console.log('\nChanging line 1 on page 1 does not emit change:');
|
|
1854
|
+
console.log('---');
|
|
1855
|
+
bookProxy.setLine({ pageIndex: 0, lineIndex: 0 }, 'a troll was born');
|
|
1856
|
+
} finally {
|
|
1857
|
+
// Stop monitoring line 3 on page 1.
|
|
1858
|
+
stateManager.releaseState(model.myBook, line3OnPage1Index);
|
|
1859
|
+
lineSubscription.unsubscribe();
|
|
1860
|
+
}
|
|
1691
1861
|
}
|
|
1692
1862
|
|
|
1693
1863
|
export const run = (() => {
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
'They lived happily ever after.',
|
|
1706
|
-
'The end'
|
|
1707
|
-
]
|
|
1708
|
-
])
|
|
1709
|
-
};
|
|
1710
|
-
testMonitorTextDocument(stateManager, stateContext);
|
|
1711
|
-
testMonitoreSpecificLineInDocument(stateManager, stateContext);
|
|
1864
|
+
const stateManager: IStateManager = InjectionContainer.get(
|
|
1865
|
+
RsXStateManagerInjectionTokens.IStateManager,
|
|
1866
|
+
);
|
|
1867
|
+
const model = {
|
|
1868
|
+
myBook: new TextDocument([
|
|
1869
|
+
['Once upon a time', 'bla bla'],
|
|
1870
|
+
['bla bla', 'They lived happily ever after.', 'The end'],
|
|
1871
|
+
]),
|
|
1872
|
+
};
|
|
1873
|
+
testMonitorTextDocument(stateManager, model);
|
|
1874
|
+
testMonitoreSpecificLineInDocument(stateManager, model);
|
|
1712
1875
|
})();
|
|
1876
|
+
|
|
1713
1877
|
```nclude_relative ../demo/src/rs-x-state-manager/register-set.ts %}
|
|
1714
|
-
|
|
1878
|
+
````
|
|
1879
|
+
|
|
1715
1880
|
**Output:**
|
|
1881
|
+
|
|
1716
1882
|
```console
|
|
1717
1883
|
Running demo: /Users/robertsanders/projects/rs-x/demo/src/rs-x-state-manager/state-manager-customize.ts
|
|
1718
1884
|
|
|
@@ -1762,4 +1928,3 @@ Page 1:
|
|
|
1762
1928
|
Changing line 1 on page 1 does not emit change:
|
|
1763
1929
|
---
|
|
1764
1930
|
```
|
|
1765
|
-
|