atom.io 0.40.10 → 0.41.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/dist/eslint-plugin/index.js +1 -1
- package/dist/internal/index.d.ts +11 -2
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +34 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/realtime/index.d.ts +2 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-client/index.js +2 -1
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-server/index.js +1 -1
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/transceivers/o-list/index.d.ts +20 -10
- package/dist/transceivers/o-list/index.d.ts.map +1 -1
- package/dist/transceivers/o-list/index.js +215 -10
- package/dist/transceivers/o-list/index.js.map +1 -1
- package/dist/transceivers/set-rtx/index.d.ts +16 -16
- package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
- package/dist/transceivers/set-rtx/index.js.map +1 -1
- package/dist/transceivers/u-list/index.d.ts +27 -18
- package/dist/transceivers/u-list/index.d.ts.map +1 -1
- package/dist/transceivers/u-list/index.js +54 -34
- package/dist/transceivers/u-list/index.js.map +1 -1
- package/package.json +10 -10
- package/src/internal/index.ts +1 -0
- package/src/internal/micro.ts +69 -0
- package/src/realtime/shared-room-store.ts +3 -2
- package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
- package/src/transceivers/o-list/o-list.ts +231 -30
- package/src/transceivers/set-rtx/set-rtx.ts +22 -21
- package/src/transceivers/u-list/u-list.ts +85 -55
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Enumeration,
|
|
3
|
+
Fn,
|
|
4
|
+
Transceiver,
|
|
5
|
+
TransceiverMode,
|
|
6
|
+
} from "atom.io/internal"
|
|
7
|
+
import { enumeration, packValue, Subject, unpackValue } from "atom.io/internal"
|
|
3
8
|
import type { primitive } from "atom.io/json"
|
|
4
9
|
|
|
5
|
-
type ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>
|
|
10
|
+
export type ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>
|
|
6
11
|
export type ArrayUpdate<P extends primitive> =
|
|
7
12
|
| {
|
|
8
13
|
type: `copyWithin`
|
|
@@ -48,9 +53,9 @@ export type ArrayUpdate<P extends primitive> =
|
|
|
48
53
|
| {
|
|
49
54
|
type: `splice`
|
|
50
55
|
start: number
|
|
51
|
-
deleteCount
|
|
52
|
-
items
|
|
53
|
-
deleted
|
|
56
|
+
deleteCount: number
|
|
57
|
+
items: readonly P[]
|
|
58
|
+
deleted: readonly P[]
|
|
54
59
|
}
|
|
55
60
|
| {
|
|
56
61
|
type: `truncate`
|
|
@@ -62,6 +67,193 @@ true satisfies ArrayMutations extends OListUpdateType
|
|
|
62
67
|
? true
|
|
63
68
|
: Exclude<ArrayMutations, OListUpdateType>
|
|
64
69
|
|
|
70
|
+
export type PackedArrayUpdate<P extends primitive> = string & {
|
|
71
|
+
update?: ArrayUpdate<P>
|
|
72
|
+
type?: P
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const ARRAY_UPDATES = [
|
|
76
|
+
// virtual methods
|
|
77
|
+
`set`,
|
|
78
|
+
`truncate`,
|
|
79
|
+
`extend`,
|
|
80
|
+
// actual methods
|
|
81
|
+
`pop`,
|
|
82
|
+
`push`,
|
|
83
|
+
`shift`,
|
|
84
|
+
`unshift`,
|
|
85
|
+
`copyWithin`,
|
|
86
|
+
`fill`,
|
|
87
|
+
`splice`,
|
|
88
|
+
`reverse`,
|
|
89
|
+
`sort`,
|
|
90
|
+
] as const
|
|
91
|
+
true satisfies ArrayUpdate<any>[`type`] extends (typeof ARRAY_UPDATES)[number]
|
|
92
|
+
? true
|
|
93
|
+
: Exclude<ArrayUpdate<any>[`type`], (typeof ARRAY_UPDATES)[number]>
|
|
94
|
+
|
|
95
|
+
export const ARRAY_UPDATE_ENUM: Enumeration<typeof ARRAY_UPDATES> =
|
|
96
|
+
enumeration(ARRAY_UPDATES)
|
|
97
|
+
|
|
98
|
+
export function packArrayUpdate<P extends primitive>(
|
|
99
|
+
update: ArrayUpdate<P>,
|
|
100
|
+
): PackedArrayUpdate<P> {
|
|
101
|
+
let packed = ARRAY_UPDATE_ENUM[update.type] + `\u001F`
|
|
102
|
+
switch (update.type) {
|
|
103
|
+
case `set`:
|
|
104
|
+
packed += update.index + `\u001E` + packValue(update.next)
|
|
105
|
+
if (update.prev !== undefined) {
|
|
106
|
+
packed += `\u001E` + packValue(update.prev)
|
|
107
|
+
}
|
|
108
|
+
return packed
|
|
109
|
+
case `truncate`:
|
|
110
|
+
return (
|
|
111
|
+
packed +
|
|
112
|
+
update.length +
|
|
113
|
+
`\u001E` +
|
|
114
|
+
update.items.map(packValue).join(`\u001E`)
|
|
115
|
+
)
|
|
116
|
+
case `extend`:
|
|
117
|
+
return packed + update.next + `\u001E` + update.prev
|
|
118
|
+
case `pop`:
|
|
119
|
+
case `shift`:
|
|
120
|
+
if (update.value !== undefined) {
|
|
121
|
+
packed += packValue(update.value)
|
|
122
|
+
}
|
|
123
|
+
return packed
|
|
124
|
+
case `push`:
|
|
125
|
+
case `unshift`:
|
|
126
|
+
return packed + update.items.map(packValue).join(`\u001E`)
|
|
127
|
+
case `copyWithin`:
|
|
128
|
+
packed += update.target + `\u001E` + update.start
|
|
129
|
+
if (update.end !== undefined) {
|
|
130
|
+
packed += `\u001E` + update.end
|
|
131
|
+
}
|
|
132
|
+
packed += `\u001E\u001E` + update.prev.map(packValue).join(`\u001E`)
|
|
133
|
+
return packed
|
|
134
|
+
case `fill`:
|
|
135
|
+
packed += packValue(update.value)
|
|
136
|
+
if (update.start !== undefined) {
|
|
137
|
+
packed += `\u001E` + update.start
|
|
138
|
+
}
|
|
139
|
+
if (update.end !== undefined) {
|
|
140
|
+
packed += `\u001E` + update.end
|
|
141
|
+
}
|
|
142
|
+
packed += `\u001E\u001E` + update.prev.map(packValue).join(`\u001E`)
|
|
143
|
+
return packed
|
|
144
|
+
case `splice`:
|
|
145
|
+
return (
|
|
146
|
+
packed +
|
|
147
|
+
update.start +
|
|
148
|
+
`\u001E\u001E` +
|
|
149
|
+
update.deleteCount +
|
|
150
|
+
`\u001E\u001E` +
|
|
151
|
+
update.items.map(packValue).join(`\u001E`) +
|
|
152
|
+
`\u001E\u001E` +
|
|
153
|
+
update.deleted.map(packValue).join(`\u001E`)
|
|
154
|
+
)
|
|
155
|
+
case `reverse`:
|
|
156
|
+
return packed
|
|
157
|
+
case `sort`:
|
|
158
|
+
return (
|
|
159
|
+
packed +
|
|
160
|
+
update.next.map(packValue).join(`\u001E`) +
|
|
161
|
+
`\u001E\u001E` +
|
|
162
|
+
update.prev.map(packValue).join(`\u001E`)
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function unpackArrayUpdate<P extends primitive>(
|
|
168
|
+
packed: PackedArrayUpdate<P>,
|
|
169
|
+
): ArrayUpdate<P> {
|
|
170
|
+
const [head, tail] = packed.split(`\u001F`) as [
|
|
171
|
+
Extract<keyof typeof ARRAY_UPDATE_ENUM, number>,
|
|
172
|
+
string,
|
|
173
|
+
]
|
|
174
|
+
const type = ARRAY_UPDATE_ENUM[head]
|
|
175
|
+
switch (type) {
|
|
176
|
+
case `set`: {
|
|
177
|
+
const [i, n, p] = tail.split(`\u001E`)
|
|
178
|
+
const index = +i
|
|
179
|
+
const next = unpackValue(n) as P
|
|
180
|
+
if (p === undefined) {
|
|
181
|
+
return { type, index, next }
|
|
182
|
+
}
|
|
183
|
+
const prev = unpackValue(p) as P
|
|
184
|
+
return { type, index, next, prev }
|
|
185
|
+
}
|
|
186
|
+
case `truncate`: {
|
|
187
|
+
const [l, ...i] = tail.split(`\u001E`)
|
|
188
|
+
const length = +l
|
|
189
|
+
const items = i.map(unpackValue) as P[]
|
|
190
|
+
return { type, length, items }
|
|
191
|
+
}
|
|
192
|
+
case `extend`: {
|
|
193
|
+
const [n, p] = tail.split(`\u001E`)
|
|
194
|
+
const next = +n
|
|
195
|
+
const prev = +p
|
|
196
|
+
return { type, next, prev }
|
|
197
|
+
}
|
|
198
|
+
case `pop`:
|
|
199
|
+
case `shift`:
|
|
200
|
+
if (tail !== ``) {
|
|
201
|
+
const value = unpackValue(tail) as P
|
|
202
|
+
return { type, value }
|
|
203
|
+
}
|
|
204
|
+
return { type }
|
|
205
|
+
case `push`:
|
|
206
|
+
case `unshift`: {
|
|
207
|
+
const items = tail.split(`\u001E`).map(unpackValue) as P[]
|
|
208
|
+
return { type, items }
|
|
209
|
+
}
|
|
210
|
+
case `copyWithin`: {
|
|
211
|
+
const [numbers, data] = tail.split(`\u001E\u001E`)
|
|
212
|
+
const prev = data ? (data.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
213
|
+
const [t, s, e] = numbers.split(`\u001E`)
|
|
214
|
+
const target = +t
|
|
215
|
+
const start = +s
|
|
216
|
+
if (e === undefined) {
|
|
217
|
+
return { type, target, start, prev }
|
|
218
|
+
}
|
|
219
|
+
const end = +e
|
|
220
|
+
return { type, target, start, prev, end }
|
|
221
|
+
}
|
|
222
|
+
case `fill`: {
|
|
223
|
+
const [numbers, data] = tail.split(`\u001E\u001E`)
|
|
224
|
+
const prev = data ? (data.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
225
|
+
const [v, s, e] = numbers.split(`\u001E`)
|
|
226
|
+
const value = unpackValue(v) as P
|
|
227
|
+
if (s === undefined && e === undefined) {
|
|
228
|
+
return { type, value, prev }
|
|
229
|
+
}
|
|
230
|
+
const start = +s
|
|
231
|
+
if (e === undefined) {
|
|
232
|
+
return { type, value, prev, start }
|
|
233
|
+
}
|
|
234
|
+
const end = +e
|
|
235
|
+
return { type, value, prev, start, end }
|
|
236
|
+
}
|
|
237
|
+
case `splice`: {
|
|
238
|
+
const [s, c, i, d] = tail.split(`\u001E\u001E`)
|
|
239
|
+
|
|
240
|
+
const start = +s
|
|
241
|
+
const deleteCount = +c
|
|
242
|
+
const items = i ? (i.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
243
|
+
const deleted = d ? (d.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
244
|
+
return { type, start, deleteCount, items, deleted }
|
|
245
|
+
}
|
|
246
|
+
case `reverse`:
|
|
247
|
+
return { type }
|
|
248
|
+
case `sort`: {
|
|
249
|
+
const [n, p] = tail.split(`\u001E\u001E`)
|
|
250
|
+
const next = n ? (n.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
251
|
+
const prev = p ? (p.split(`\u001E`).map(unpackValue) as P[]) : []
|
|
252
|
+
return { type, next, prev }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
65
257
|
export type ArrayMutationHandler = {
|
|
66
258
|
[K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn
|
|
67
259
|
}
|
|
@@ -69,13 +261,11 @@ export type ArrayMutationHandler = {
|
|
|
69
261
|
export class OList<P extends primitive>
|
|
70
262
|
extends Array<P>
|
|
71
263
|
implements
|
|
72
|
-
Transceiver<ReadonlyArray<P>,
|
|
264
|
+
Transceiver<ReadonlyArray<P>, PackedArrayUpdate<P>, ReadonlyArray<P>>,
|
|
73
265
|
ArrayMutationHandler
|
|
74
266
|
{
|
|
75
267
|
public mode: TransceiverMode = `record`
|
|
76
|
-
public readonly subject: Subject<
|
|
77
|
-
ArrayUpdate<P>
|
|
78
|
-
>()
|
|
268
|
+
public readonly subject: Subject<PackedArrayUpdate<P>> = new Subject()
|
|
79
269
|
|
|
80
270
|
public readonly READONLY_VIEW: ReadonlyArray<P> = this
|
|
81
271
|
|
|
@@ -198,6 +388,27 @@ export class OList<P extends primitive>
|
|
|
198
388
|
return this
|
|
199
389
|
}
|
|
200
390
|
|
|
391
|
+
public fill(value: P, start?: number, end?: number): this {
|
|
392
|
+
if (this.mode === `record`) {
|
|
393
|
+
this.mode = `playback`
|
|
394
|
+
const prev = this.slice(start, end)
|
|
395
|
+
super.fill(value, start, end)
|
|
396
|
+
if (start === undefined) {
|
|
397
|
+
this.emit({ type: `fill`, value, prev })
|
|
398
|
+
} else {
|
|
399
|
+
if (end === undefined) {
|
|
400
|
+
this.emit({ type: `fill`, value, start, prev })
|
|
401
|
+
} else {
|
|
402
|
+
this.emit({ type: `fill`, value, start, end, prev })
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
this.mode = `record`
|
|
406
|
+
} else {
|
|
407
|
+
super.fill(value, start, end)
|
|
408
|
+
}
|
|
409
|
+
return this
|
|
410
|
+
}
|
|
411
|
+
|
|
201
412
|
public sort(compareFn?: (a: P, b: P) => number): this {
|
|
202
413
|
if (this.mode === `record`) {
|
|
203
414
|
this.mode = `playback`
|
|
@@ -256,13 +467,13 @@ export class OList<P extends primitive>
|
|
|
256
467
|
|
|
257
468
|
public subscribe(
|
|
258
469
|
key: string,
|
|
259
|
-
fn: (update:
|
|
470
|
+
fn: (update: PackedArrayUpdate<P>) => void,
|
|
260
471
|
): () => void {
|
|
261
472
|
return this.subject.subscribe(key, fn)
|
|
262
473
|
}
|
|
263
474
|
|
|
264
475
|
public emit(update: ArrayUpdate<P>): void {
|
|
265
|
-
this.subject.next(update)
|
|
476
|
+
this.subject.next(packArrayUpdate(update))
|
|
266
477
|
}
|
|
267
478
|
|
|
268
479
|
private doStep(update: ArrayUpdate<P>): void {
|
|
@@ -296,11 +507,7 @@ export class OList<P extends primitive>
|
|
|
296
507
|
this.length = update.next.length
|
|
297
508
|
break
|
|
298
509
|
case `splice`:
|
|
299
|
-
|
|
300
|
-
this.splice(update.start, update.deleteCount, ...update.items)
|
|
301
|
-
} else {
|
|
302
|
-
this.splice(update.start)
|
|
303
|
-
}
|
|
510
|
+
this.splice(update.start, update.deleteCount, ...update.items)
|
|
304
511
|
break
|
|
305
512
|
case `truncate`:
|
|
306
513
|
this.length = update.length
|
|
@@ -314,9 +521,10 @@ export class OList<P extends primitive>
|
|
|
314
521
|
}
|
|
315
522
|
}
|
|
316
523
|
|
|
317
|
-
public do(update:
|
|
524
|
+
public do(update: PackedArrayUpdate<P>): null {
|
|
318
525
|
this.mode = `playback`
|
|
319
|
-
|
|
526
|
+
const unpacked = unpackArrayUpdate(update)
|
|
527
|
+
this.doStep(unpacked)
|
|
320
528
|
this.mode = `record`
|
|
321
529
|
return null
|
|
322
530
|
}
|
|
@@ -378,15 +586,7 @@ export class OList<P extends primitive>
|
|
|
378
586
|
}
|
|
379
587
|
break
|
|
380
588
|
case `splice`:
|
|
381
|
-
|
|
382
|
-
if (update.items) {
|
|
383
|
-
this.splice(update.start, update.items.length, ...update.deleted)
|
|
384
|
-
} else {
|
|
385
|
-
this.splice(update.start, 0, ...update.deleted)
|
|
386
|
-
}
|
|
387
|
-
} else if (update.items) {
|
|
388
|
-
this.splice(update.start, update.items.length)
|
|
389
|
-
}
|
|
589
|
+
this.splice(update.start, update.items.length, ...update.deleted)
|
|
390
590
|
break
|
|
391
591
|
case `truncate`:
|
|
392
592
|
this.push(...update.items)
|
|
@@ -403,9 +603,10 @@ export class OList<P extends primitive>
|
|
|
403
603
|
}
|
|
404
604
|
}
|
|
405
605
|
|
|
406
|
-
public undo(update:
|
|
606
|
+
public undo(update: PackedArrayUpdate<P>): number | null {
|
|
407
607
|
this.mode = `playback`
|
|
408
|
-
|
|
608
|
+
const unpacked = unpackArrayUpdate(update)
|
|
609
|
+
this.undoStep(unpacked)
|
|
409
610
|
this.mode = `record`
|
|
410
611
|
return null
|
|
411
612
|
}
|
|
@@ -4,11 +4,11 @@ import type { Json, primitive } from "atom.io/json"
|
|
|
4
4
|
import { stringifyJson } from "atom.io/json"
|
|
5
5
|
|
|
6
6
|
export type SetUpdateType = `add` | `clear` | `del` | `tx`
|
|
7
|
-
export type
|
|
8
|
-
export type
|
|
7
|
+
export type SetUpdateString = `${SetUpdateType}:${string}`
|
|
8
|
+
export type NumberedSetUpdateString = `${number | `*`}=${SetUpdateString}`
|
|
9
9
|
|
|
10
10
|
export interface SetRTXView<P extends primitive> extends ReadonlySet<P> {
|
|
11
|
-
readonly cache: ReadonlyArray<
|
|
11
|
+
readonly cache: ReadonlyArray<NumberedSetUpdateString | null>
|
|
12
12
|
readonly cacheLimit: number
|
|
13
13
|
readonly cacheIdx: number
|
|
14
14
|
readonly cacheUpdateNumber: number
|
|
@@ -16,7 +16,7 @@ export interface SetRTXView<P extends primitive> extends ReadonlySet<P> {
|
|
|
16
16
|
|
|
17
17
|
export interface SetRTXJson<P extends primitive> extends Json.Object {
|
|
18
18
|
members: P[]
|
|
19
|
-
cache: (
|
|
19
|
+
cache: (NumberedSetUpdateString | null)[]
|
|
20
20
|
cacheLimit: number
|
|
21
21
|
cacheIdx: number
|
|
22
22
|
cacheUpdateNumber: number
|
|
@@ -24,13 +24,14 @@ export interface SetRTXJson<P extends primitive> extends Json.Object {
|
|
|
24
24
|
export class SetRTX<P extends primitive>
|
|
25
25
|
extends Set<P>
|
|
26
26
|
implements
|
|
27
|
-
Transceiver<SetRTXView<P>,
|
|
27
|
+
Transceiver<SetRTXView<P>, NumberedSetUpdateString, SetRTXJson<P>>,
|
|
28
28
|
Lineage
|
|
29
29
|
{
|
|
30
30
|
public mode: TransceiverMode = `record`
|
|
31
|
-
public readonly subject: Subject<
|
|
31
|
+
public readonly subject: Subject<SetUpdateString> =
|
|
32
|
+
new Subject<SetUpdateString>()
|
|
32
33
|
public cacheLimit = 0
|
|
33
|
-
public cache: (
|
|
34
|
+
public cache: (NumberedSetUpdateString | null)[] = []
|
|
34
35
|
public cacheIdx = -1
|
|
35
36
|
public cacheUpdateNumber = -1
|
|
36
37
|
|
|
@@ -100,7 +101,7 @@ export class SetRTX<P extends primitive>
|
|
|
100
101
|
|
|
101
102
|
public readonly parent: SetRTX<P> | null = null
|
|
102
103
|
public child: SetRTX<P> | null = null
|
|
103
|
-
public transactionUpdates:
|
|
104
|
+
public transactionUpdates: SetUpdateString[] | null = null
|
|
104
105
|
public transaction(run: (child: SetRTX<P>) => boolean): void {
|
|
105
106
|
this.mode = `transaction`
|
|
106
107
|
this.transactionUpdates = []
|
|
@@ -134,24 +135,24 @@ export class SetRTX<P extends primitive>
|
|
|
134
135
|
|
|
135
136
|
protected _subscribe(
|
|
136
137
|
key: string,
|
|
137
|
-
fn: (update:
|
|
138
|
+
fn: (update: SetUpdateString) => void,
|
|
138
139
|
): () => void {
|
|
139
140
|
return this.subject.subscribe(key, fn)
|
|
140
141
|
}
|
|
141
142
|
public subscribe(
|
|
142
143
|
key: string,
|
|
143
|
-
fn: (update:
|
|
144
|
+
fn: (update: NumberedSetUpdateString) => void,
|
|
144
145
|
): () => void {
|
|
145
146
|
return this.subject.subscribe(key, (update) => {
|
|
146
147
|
fn(`${this.cacheUpdateNumber}=${update}`)
|
|
147
148
|
})
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
public emit(update:
|
|
151
|
+
public emit(update: SetUpdateString): void {
|
|
151
152
|
this.subject.next(update)
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
private doStep(update:
|
|
155
|
+
private doStep(update: SetUpdateString): void {
|
|
155
156
|
const typeValueBreak = update.indexOf(`:`)
|
|
156
157
|
const type = update.substring(0, typeValueBreak) as SetUpdateType
|
|
157
158
|
const value = update.substring(typeValueBreak + 1)
|
|
@@ -167,17 +168,17 @@ export class SetRTX<P extends primitive>
|
|
|
167
168
|
break
|
|
168
169
|
case `tx`:
|
|
169
170
|
for (const subUpdate of value.split(`;`)) {
|
|
170
|
-
this.doStep(subUpdate as
|
|
171
|
+
this.doStep(subUpdate as SetUpdateString)
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
public getUpdateNumber(update:
|
|
176
|
+
public getUpdateNumber(update: NumberedSetUpdateString): number {
|
|
176
177
|
const breakpoint = update.indexOf(`=`)
|
|
177
178
|
return Number(update.substring(0, breakpoint))
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
public do(update:
|
|
181
|
+
public do(update: NumberedSetUpdateString): number | `OUT_OF_RANGE` | null {
|
|
181
182
|
const breakpoint = update.indexOf(`=`)
|
|
182
183
|
const updateNumber = Number(update.substring(0, breakpoint))
|
|
183
184
|
const eventOffset = updateNumber - this.cacheUpdateNumber
|
|
@@ -185,7 +186,7 @@ export class SetRTX<P extends primitive>
|
|
|
185
186
|
if (isFuture || Number.isNaN(eventOffset)) {
|
|
186
187
|
if (eventOffset === 1 || Number.isNaN(eventOffset)) {
|
|
187
188
|
this.mode = `playback`
|
|
188
|
-
const innerUpdate = update.substring(breakpoint + 1) as
|
|
189
|
+
const innerUpdate = update.substring(breakpoint + 1) as SetUpdateString
|
|
189
190
|
this.doStep(innerUpdate)
|
|
190
191
|
this.mode = `record`
|
|
191
192
|
this.cacheUpdateNumber = updateNumber
|
|
@@ -211,7 +212,7 @@ export class SetRTX<P extends primitive>
|
|
|
211
212
|
this.undo(u)
|
|
212
213
|
done = this.cacheIdx === eventIdx - 1
|
|
213
214
|
}
|
|
214
|
-
const innerUpdate = update.substring(breakpoint + 1) as
|
|
215
|
+
const innerUpdate = update.substring(breakpoint + 1) as SetUpdateString
|
|
215
216
|
this.doStep(innerUpdate)
|
|
216
217
|
this.mode = `record`
|
|
217
218
|
this.cacheUpdateNumber = updateNumber
|
|
@@ -220,7 +221,7 @@ export class SetRTX<P extends primitive>
|
|
|
220
221
|
return `OUT_OF_RANGE`
|
|
221
222
|
}
|
|
222
223
|
|
|
223
|
-
public undoStep(update:
|
|
224
|
+
public undoStep(update: SetUpdateString): void {
|
|
224
225
|
const breakpoint = update.indexOf(`:`)
|
|
225
226
|
const type = update.substring(0, breakpoint) as SetUpdateType
|
|
226
227
|
const value = update.substring(breakpoint + 1)
|
|
@@ -237,7 +238,7 @@ export class SetRTX<P extends primitive>
|
|
|
237
238
|
break
|
|
238
239
|
}
|
|
239
240
|
case `tx`: {
|
|
240
|
-
const updates = value.split(`;`) as
|
|
241
|
+
const updates = value.split(`;`) as SetUpdateString[]
|
|
241
242
|
for (let i = updates.length - 1; i >= 0; i--) {
|
|
242
243
|
this.undoStep(updates[i])
|
|
243
244
|
}
|
|
@@ -245,12 +246,12 @@ export class SetRTX<P extends primitive>
|
|
|
245
246
|
}
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
public undo(update:
|
|
249
|
+
public undo(update: NumberedSetUpdateString): number | null {
|
|
249
250
|
const breakpoint = update.indexOf(`=`)
|
|
250
251
|
const updateNumber = Number(update.substring(0, breakpoint))
|
|
251
252
|
if (updateNumber === this.cacheUpdateNumber) {
|
|
252
253
|
this.mode = `playback`
|
|
253
|
-
const innerUpdate = update.substring(breakpoint + 1) as
|
|
254
|
+
const innerUpdate = update.substring(breakpoint + 1) as SetUpdateString
|
|
254
255
|
this.undoStep(innerUpdate)
|
|
255
256
|
this.mode = `record`
|
|
256
257
|
this.cacheUpdateNumber--
|
|
@@ -1,24 +1,68 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Enumeration,
|
|
3
|
+
Fn,
|
|
4
|
+
Transceiver,
|
|
5
|
+
TransceiverMode,
|
|
6
|
+
} from "atom.io/internal"
|
|
7
|
+
import { enumeration, packValue, Subject, unpackValue } from "atom.io/internal"
|
|
8
|
+
import type { primitive } from "atom.io/json"
|
|
9
|
+
|
|
10
|
+
export type SetMutations = Exclude<
|
|
11
|
+
keyof Set<any>,
|
|
12
|
+
symbol | keyof ReadonlySet<any>
|
|
13
|
+
>
|
|
14
|
+
export type SetUpdate<P extends primitive> =
|
|
15
|
+
| {
|
|
16
|
+
type: `add` | `delete`
|
|
17
|
+
value: P
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
type: `clear`
|
|
21
|
+
values: P[]
|
|
22
|
+
}
|
|
23
|
+
export type UListUpdateType = SetUpdate<any>[`type`]
|
|
24
|
+
true satisfies SetMutations extends UListUpdateType
|
|
25
|
+
? true
|
|
26
|
+
: Exclude<SetMutations, UListUpdateType>
|
|
27
|
+
|
|
28
|
+
export type PackedSetUpdate<P extends primitive> = string & {
|
|
29
|
+
update?: SetUpdate<P>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const SET_UPDATE_ENUM: Enumeration<[`add`, `delete`, `clear`]> =
|
|
33
|
+
enumeration([`add`, `delete`, `clear`] as const)
|
|
34
|
+
|
|
35
|
+
export function packSetUpdate<P extends primitive>(
|
|
36
|
+
update: SetUpdate<P>,
|
|
37
|
+
): PackedSetUpdate<P> {
|
|
38
|
+
const head = SET_UPDATE_ENUM[update.type] + `\u001F`
|
|
39
|
+
if (update.type === `clear`) {
|
|
40
|
+
return head + update.values.map(packValue).join(`\u001E`)
|
|
41
|
+
}
|
|
42
|
+
return head + packValue(update.value)
|
|
43
|
+
}
|
|
44
|
+
export function unpackSetUpdate<P extends primitive>(
|
|
45
|
+
packed: PackedSetUpdate<P>,
|
|
46
|
+
): SetUpdate<P> {
|
|
47
|
+
const [type, tail] = packed.split(`\u001F`) as [0 | 1 | 2, string]
|
|
48
|
+
const head = SET_UPDATE_ENUM[type]
|
|
49
|
+
if (head === `clear`) {
|
|
50
|
+
const values = tail.split(`\u001E`).map(unpackValue) as P[]
|
|
51
|
+
return { type: `clear`, values }
|
|
52
|
+
}
|
|
53
|
+
return { type: head, value: unpackValue(tail) as P }
|
|
13
54
|
}
|
|
55
|
+
|
|
56
|
+
export type SetMutationHandler = { [K in UListUpdateType]: Fn }
|
|
57
|
+
|
|
14
58
|
export class UList<P extends primitive>
|
|
15
59
|
extends Set<P>
|
|
16
|
-
implements
|
|
60
|
+
implements
|
|
61
|
+
Transceiver<ReadonlySet<P>, PackedSetUpdate<P>, ReadonlyArray<P>>,
|
|
62
|
+
SetMutationHandler
|
|
17
63
|
{
|
|
18
64
|
public mode: TransceiverMode = `record`
|
|
19
|
-
public readonly subject: Subject<
|
|
20
|
-
UListUpdate<P>
|
|
21
|
-
>()
|
|
65
|
+
public readonly subject: Subject<PackedSetUpdate<P>> = new Subject()
|
|
22
66
|
|
|
23
67
|
public constructor(values?: Iterable<P>) {
|
|
24
68
|
super(values)
|
|
@@ -28,20 +72,18 @@ export class UList<P extends primitive>
|
|
|
28
72
|
|
|
29
73
|
public readonly READONLY_VIEW: ReadonlySet<P> = this
|
|
30
74
|
|
|
31
|
-
public toJSON():
|
|
32
|
-
return
|
|
33
|
-
members: [...this],
|
|
34
|
-
}
|
|
75
|
+
public toJSON(): ReadonlyArray<P> {
|
|
76
|
+
return [...this]
|
|
35
77
|
}
|
|
36
78
|
|
|
37
|
-
public static fromJSON<P extends primitive>(json:
|
|
38
|
-
return new UList<P>(json
|
|
79
|
+
public static fromJSON<P extends primitive>(json: ReadonlyArray<P>): UList<P> {
|
|
80
|
+
return new UList<P>(json)
|
|
39
81
|
}
|
|
40
82
|
|
|
41
83
|
public add(value: P): this {
|
|
42
84
|
const result = super.add(value)
|
|
43
85
|
if (this.mode === `record`) {
|
|
44
|
-
this.emit(`add
|
|
86
|
+
this.emit({ type: `add`, value })
|
|
45
87
|
}
|
|
46
88
|
return result
|
|
47
89
|
}
|
|
@@ -50,73 +92,61 @@ export class UList<P extends primitive>
|
|
|
50
92
|
const capturedContents = this.mode === `record` ? [...this] : null
|
|
51
93
|
super.clear()
|
|
52
94
|
if (capturedContents) {
|
|
53
|
-
this.emit(`clear
|
|
95
|
+
this.emit({ type: `clear`, values: capturedContents })
|
|
54
96
|
}
|
|
55
97
|
}
|
|
56
98
|
|
|
57
99
|
public delete(value: P): boolean {
|
|
58
100
|
const result = super.delete(value)
|
|
59
101
|
if (this.mode === `record`) {
|
|
60
|
-
this.emit(`
|
|
102
|
+
this.emit({ type: `delete`, value })
|
|
61
103
|
}
|
|
62
104
|
return result
|
|
63
105
|
}
|
|
64
106
|
|
|
65
107
|
public subscribe(
|
|
66
108
|
key: string,
|
|
67
|
-
fn: (update:
|
|
109
|
+
fn: (update: PackedSetUpdate<P>) => void,
|
|
68
110
|
): () => void {
|
|
69
111
|
return this.subject.subscribe(key, fn)
|
|
70
112
|
}
|
|
71
113
|
|
|
72
|
-
public emit(update:
|
|
73
|
-
this.subject.next(update)
|
|
114
|
+
public emit(update: SetUpdate<P>): void {
|
|
115
|
+
this.subject.next(packSetUpdate(update))
|
|
74
116
|
}
|
|
75
117
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
switch (type) {
|
|
118
|
+
public do(packed: PackedSetUpdate<P>): null {
|
|
119
|
+
this.mode = `playback`
|
|
120
|
+
const update = unpackSetUpdate(packed)
|
|
121
|
+
switch (update.type) {
|
|
81
122
|
case `add`:
|
|
82
|
-
this.add(
|
|
123
|
+
this.add(update.value)
|
|
83
124
|
break
|
|
84
|
-
case `
|
|
85
|
-
this.delete(
|
|
125
|
+
case `delete`:
|
|
126
|
+
this.delete(update.value)
|
|
86
127
|
break
|
|
87
128
|
case `clear`:
|
|
88
129
|
this.clear()
|
|
89
130
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public do(update: UListUpdate<P>): null {
|
|
93
|
-
this.mode = `playback`
|
|
94
|
-
this.doStep(update)
|
|
95
131
|
this.mode = `record`
|
|
96
132
|
return null
|
|
97
133
|
}
|
|
98
134
|
|
|
99
|
-
public
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
switch (type) {
|
|
135
|
+
public undo(packed: PackedSetUpdate<P>): number | null {
|
|
136
|
+
const update = unpackSetUpdate(packed)
|
|
137
|
+
this.mode = `playback`
|
|
138
|
+
switch (update.type) {
|
|
104
139
|
case `add`:
|
|
105
|
-
this.delete(
|
|
140
|
+
this.delete(update.value)
|
|
106
141
|
break
|
|
107
|
-
case `
|
|
108
|
-
this.add(
|
|
142
|
+
case `delete`:
|
|
143
|
+
this.add(update.value)
|
|
109
144
|
break
|
|
110
145
|
case `clear`: {
|
|
111
|
-
const values =
|
|
146
|
+
const values = update.values
|
|
112
147
|
for (const v of values) this.add(v)
|
|
113
148
|
}
|
|
114
149
|
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
public undo(update: UListUpdate<P>): number | null {
|
|
118
|
-
this.mode = `playback`
|
|
119
|
-
this.undoStep(update)
|
|
120
150
|
this.mode = `record`
|
|
121
151
|
return null
|
|
122
152
|
}
|