atom.io 0.40.9 → 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 +87 -0
- package/dist/transceivers/o-list/index.d.ts.map +1 -0
- package/dist/transceivers/o-list/index.js +513 -0
- package/dist/transceivers/o-list/index.js.map +1 -0
- 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 +17 -13
- 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/index.ts +1 -0
- package/src/transceivers/o-list/o-list.ts +613 -0
- package/src/transceivers/set-rtx/set-rtx.ts +22 -21
- package/src/transceivers/u-list/u-list.ts +85 -55
|
@@ -0,0 +1,613 @@
|
|
|
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 ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>
|
|
11
|
+
export type ArrayUpdate<P extends primitive> =
|
|
12
|
+
| {
|
|
13
|
+
type: `copyWithin`
|
|
14
|
+
target: number
|
|
15
|
+
start: number
|
|
16
|
+
end?: number
|
|
17
|
+
prev: readonly P[]
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
type: `extend`
|
|
21
|
+
next: number
|
|
22
|
+
prev: number
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
type: `fill`
|
|
26
|
+
value: P
|
|
27
|
+
start?: number
|
|
28
|
+
end?: number
|
|
29
|
+
prev: readonly P[]
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
type: `pop` | `shift`
|
|
33
|
+
value?: P
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
type: `push` | `unshift`
|
|
37
|
+
items: readonly P[]
|
|
38
|
+
}
|
|
39
|
+
| {
|
|
40
|
+
type: `reverse`
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
type: `set`
|
|
44
|
+
next: P
|
|
45
|
+
prev?: P
|
|
46
|
+
index: number
|
|
47
|
+
}
|
|
48
|
+
| {
|
|
49
|
+
type: `sort`
|
|
50
|
+
next: readonly P[]
|
|
51
|
+
prev: readonly P[]
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
type: `splice`
|
|
55
|
+
start: number
|
|
56
|
+
deleteCount: number
|
|
57
|
+
items: readonly P[]
|
|
58
|
+
deleted: readonly P[]
|
|
59
|
+
}
|
|
60
|
+
| {
|
|
61
|
+
type: `truncate`
|
|
62
|
+
length: number
|
|
63
|
+
items: readonly P[]
|
|
64
|
+
}
|
|
65
|
+
export type OListUpdateType = ArrayUpdate<any>[`type`]
|
|
66
|
+
true satisfies ArrayMutations extends OListUpdateType
|
|
67
|
+
? true
|
|
68
|
+
: Exclude<ArrayMutations, OListUpdateType>
|
|
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
|
+
|
|
257
|
+
export type ArrayMutationHandler = {
|
|
258
|
+
[K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export class OList<P extends primitive>
|
|
262
|
+
extends Array<P>
|
|
263
|
+
implements
|
|
264
|
+
Transceiver<ReadonlyArray<P>, PackedArrayUpdate<P>, ReadonlyArray<P>>,
|
|
265
|
+
ArrayMutationHandler
|
|
266
|
+
{
|
|
267
|
+
public mode: TransceiverMode = `record`
|
|
268
|
+
public readonly subject: Subject<PackedArrayUpdate<P>> = new Subject()
|
|
269
|
+
|
|
270
|
+
public readonly READONLY_VIEW: ReadonlyArray<P> = this
|
|
271
|
+
|
|
272
|
+
public constructor(arrayLength?: number)
|
|
273
|
+
public constructor(...items: P[])
|
|
274
|
+
public constructor(...items: P[]) {
|
|
275
|
+
super(...items)
|
|
276
|
+
// biome-ignore lint/correctness/noConstructorReturn: this is chill
|
|
277
|
+
return new Proxy(this, {
|
|
278
|
+
set: (target, prop, value, receiver) => {
|
|
279
|
+
if (
|
|
280
|
+
typeof prop === `string` &&
|
|
281
|
+
!Number.isNaN(Number.parseInt(prop, 10))
|
|
282
|
+
) {
|
|
283
|
+
const index = Number(prop)
|
|
284
|
+
let prev: P | undefined
|
|
285
|
+
if (this.mode === `record`) {
|
|
286
|
+
prev = target[index]
|
|
287
|
+
}
|
|
288
|
+
target[index] = value
|
|
289
|
+
if (prev) {
|
|
290
|
+
this.emit({ type: `set`, index, next: value, prev })
|
|
291
|
+
} else if (this.mode === `record`) {
|
|
292
|
+
this.emit({ type: `set`, index, next: value })
|
|
293
|
+
}
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
if (prop === `length`) {
|
|
297
|
+
if (this.mode === `record`) {
|
|
298
|
+
const prevLength = target.length
|
|
299
|
+
if (prevLength === value) return true
|
|
300
|
+
if (prevLength > value) {
|
|
301
|
+
const dropped = target.slice(value)
|
|
302
|
+
target.length = value
|
|
303
|
+
this.emit({ type: `truncate`, length: value, items: dropped })
|
|
304
|
+
} else {
|
|
305
|
+
target.length = value
|
|
306
|
+
this.emit({ type: `extend`, next: value, prev: prevLength })
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
target.length = value
|
|
310
|
+
}
|
|
311
|
+
return true
|
|
312
|
+
}
|
|
313
|
+
return Reflect.set(target, prop, value, receiver)
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public toJSON(): ReadonlyArray<P> {
|
|
319
|
+
return [...this]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
public static fromJSON<P extends primitive>(json: ReadonlyArray<P>): OList<P> {
|
|
323
|
+
return new OList<P>(...json)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
public push(...items: P[]): number {
|
|
327
|
+
let result: number
|
|
328
|
+
if (this.mode === `record`) {
|
|
329
|
+
this.mode = `playback`
|
|
330
|
+
result = super.push(...items)
|
|
331
|
+
this.mode = `record`
|
|
332
|
+
this.emit({ type: `push`, items })
|
|
333
|
+
} else {
|
|
334
|
+
result = super.push(...items)
|
|
335
|
+
}
|
|
336
|
+
return result
|
|
337
|
+
}
|
|
338
|
+
public pop(): P | undefined {
|
|
339
|
+
let value: P | undefined
|
|
340
|
+
if (this.mode === `record`) {
|
|
341
|
+
this.mode = `playback`
|
|
342
|
+
value = super.pop()
|
|
343
|
+
if (value === undefined) {
|
|
344
|
+
this.emit({ type: `pop` })
|
|
345
|
+
} else {
|
|
346
|
+
this.emit({ type: `pop`, value })
|
|
347
|
+
}
|
|
348
|
+
this.mode = `record`
|
|
349
|
+
} else {
|
|
350
|
+
value = super.pop()
|
|
351
|
+
}
|
|
352
|
+
return value
|
|
353
|
+
}
|
|
354
|
+
public shift(): P | undefined {
|
|
355
|
+
let value: P | undefined
|
|
356
|
+
if (this.mode === `record`) {
|
|
357
|
+
this.mode = `playback`
|
|
358
|
+
value = super.shift()
|
|
359
|
+
if (value === undefined) {
|
|
360
|
+
this.emit({ type: `shift` })
|
|
361
|
+
} else {
|
|
362
|
+
this.emit({ type: `shift`, value })
|
|
363
|
+
}
|
|
364
|
+
this.mode = `record`
|
|
365
|
+
} else {
|
|
366
|
+
value = super.shift()
|
|
367
|
+
}
|
|
368
|
+
return value
|
|
369
|
+
}
|
|
370
|
+
public unshift(...items: P[]): number {
|
|
371
|
+
let result: number
|
|
372
|
+
if (this.mode === `record`) {
|
|
373
|
+
this.mode = `playback`
|
|
374
|
+
result = super.unshift(...items)
|
|
375
|
+
this.emit({ type: `unshift`, items })
|
|
376
|
+
this.mode = `record`
|
|
377
|
+
} else {
|
|
378
|
+
result = super.unshift(...items)
|
|
379
|
+
}
|
|
380
|
+
return result
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
public reverse(): this {
|
|
384
|
+
super.reverse()
|
|
385
|
+
if (this.mode === `record`) {
|
|
386
|
+
this.emit({ type: `reverse` })
|
|
387
|
+
}
|
|
388
|
+
return this
|
|
389
|
+
}
|
|
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
|
+
|
|
412
|
+
public sort(compareFn?: (a: P, b: P) => number): this {
|
|
413
|
+
if (this.mode === `record`) {
|
|
414
|
+
this.mode = `playback`
|
|
415
|
+
const prev = [...this]
|
|
416
|
+
super.sort(compareFn)
|
|
417
|
+
const next = [...this]
|
|
418
|
+
this.emit({ type: `sort`, next, prev })
|
|
419
|
+
this.mode = `record`
|
|
420
|
+
}
|
|
421
|
+
return this
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
public splice(start: number, deleteCount?: number): P[]
|
|
425
|
+
public splice(start: number, deleteCount: number, ...items: P[]): P[]
|
|
426
|
+
public splice(
|
|
427
|
+
...params: [start: number, deleteCount?: number, ...items: P[]]
|
|
428
|
+
): P[] {
|
|
429
|
+
const [start, deleteCount, ...items] = params
|
|
430
|
+
const originalMode = this.mode
|
|
431
|
+
if (originalMode === `record`) this.mode = `playback`
|
|
432
|
+
const deleted = super.splice(...(params as [number, number, ...P[]]))
|
|
433
|
+
if (originalMode === `record`) this.mode = `record`
|
|
434
|
+
if (deleteCount === undefined) {
|
|
435
|
+
this.emit({
|
|
436
|
+
type: `splice`,
|
|
437
|
+
start,
|
|
438
|
+
items,
|
|
439
|
+
deleted,
|
|
440
|
+
deleteCount: deleted.length,
|
|
441
|
+
})
|
|
442
|
+
} else {
|
|
443
|
+
this.emit({ type: `splice`, start, items, deleted, deleteCount })
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return deleted
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
public copyWithin(target: number, start: number, end?: number): this {
|
|
450
|
+
const originalMode = this.mode
|
|
451
|
+
let prev: P[] | undefined
|
|
452
|
+
if (originalMode === `record`) {
|
|
453
|
+
prev = this.slice(target)
|
|
454
|
+
this.mode = `playback`
|
|
455
|
+
}
|
|
456
|
+
super.copyWithin(target, start, end)
|
|
457
|
+
if (originalMode === `record`) this.mode = `record`
|
|
458
|
+
if (prev) {
|
|
459
|
+
if (end === undefined) {
|
|
460
|
+
this.emit({ type: `copyWithin`, prev, target, start })
|
|
461
|
+
} else {
|
|
462
|
+
this.emit({ type: `copyWithin`, prev, target, start, end })
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return this
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
public subscribe(
|
|
469
|
+
key: string,
|
|
470
|
+
fn: (update: PackedArrayUpdate<P>) => void,
|
|
471
|
+
): () => void {
|
|
472
|
+
return this.subject.subscribe(key, fn)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
public emit(update: ArrayUpdate<P>): void {
|
|
476
|
+
this.subject.next(packArrayUpdate(update))
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
private doStep(update: ArrayUpdate<P>): void {
|
|
480
|
+
const type = update.type
|
|
481
|
+
switch (type) {
|
|
482
|
+
case `copyWithin`:
|
|
483
|
+
this.copyWithin(update.target, update.start, update.end)
|
|
484
|
+
break
|
|
485
|
+
case `extend`:
|
|
486
|
+
this.length = update.next
|
|
487
|
+
break
|
|
488
|
+
case `fill`:
|
|
489
|
+
this.fill(update.value, update.start, update.end)
|
|
490
|
+
break
|
|
491
|
+
case `pop`:
|
|
492
|
+
this.pop()
|
|
493
|
+
break
|
|
494
|
+
case `push`:
|
|
495
|
+
this.push(...update.items)
|
|
496
|
+
break
|
|
497
|
+
case `reverse`:
|
|
498
|
+
this.reverse()
|
|
499
|
+
break
|
|
500
|
+
case `shift`:
|
|
501
|
+
this.shift()
|
|
502
|
+
break
|
|
503
|
+
case `sort`:
|
|
504
|
+
for (let i = 0; i < update.next.length; i++) {
|
|
505
|
+
this[i] = update.next[i]
|
|
506
|
+
}
|
|
507
|
+
this.length = update.next.length
|
|
508
|
+
break
|
|
509
|
+
case `splice`:
|
|
510
|
+
this.splice(update.start, update.deleteCount, ...update.items)
|
|
511
|
+
break
|
|
512
|
+
case `truncate`:
|
|
513
|
+
this.length = update.length
|
|
514
|
+
break
|
|
515
|
+
case `set`:
|
|
516
|
+
this[update.index] = update.next
|
|
517
|
+
break
|
|
518
|
+
case `unshift`:
|
|
519
|
+
this.unshift(...update.items)
|
|
520
|
+
break
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
public do(update: PackedArrayUpdate<P>): null {
|
|
525
|
+
this.mode = `playback`
|
|
526
|
+
const unpacked = unpackArrayUpdate(update)
|
|
527
|
+
this.doStep(unpacked)
|
|
528
|
+
this.mode = `record`
|
|
529
|
+
return null
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
public undoStep(update: ArrayUpdate<P>): void {
|
|
533
|
+
switch (update.type) {
|
|
534
|
+
case `copyWithin`:
|
|
535
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
536
|
+
this[i + update.target] = update.prev[i]
|
|
537
|
+
}
|
|
538
|
+
break
|
|
539
|
+
case `extend`:
|
|
540
|
+
this.length = update.prev
|
|
541
|
+
break
|
|
542
|
+
case `fill`:
|
|
543
|
+
{
|
|
544
|
+
const start = update.start ?? 0
|
|
545
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
546
|
+
this[i + start] = update.prev[i]
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
break
|
|
550
|
+
case `pop`:
|
|
551
|
+
if (update.value) this.push(update.value)
|
|
552
|
+
break
|
|
553
|
+
case `push`:
|
|
554
|
+
{
|
|
555
|
+
let i = update.items.length - 1
|
|
556
|
+
while (i >= 0) {
|
|
557
|
+
this.pop()
|
|
558
|
+
--i
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
break
|
|
562
|
+
case `reverse`:
|
|
563
|
+
this.reverse()
|
|
564
|
+
break
|
|
565
|
+
case `shift`:
|
|
566
|
+
if (update.value) this.unshift(update.value)
|
|
567
|
+
break
|
|
568
|
+
case `sort`:
|
|
569
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
570
|
+
this[i] = update.prev[i]
|
|
571
|
+
}
|
|
572
|
+
this.length = update.prev.length
|
|
573
|
+
break
|
|
574
|
+
case `set`:
|
|
575
|
+
if (update.prev) {
|
|
576
|
+
this[update.index] = update.prev
|
|
577
|
+
} else {
|
|
578
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
579
|
+
delete this[update.index]
|
|
580
|
+
const firstEmptyIndex = this.findIndex(
|
|
581
|
+
(_, i) => !Object.hasOwn(this, i),
|
|
582
|
+
)
|
|
583
|
+
if (firstEmptyIndex !== -1) {
|
|
584
|
+
this.length = firstEmptyIndex
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
break
|
|
588
|
+
case `splice`:
|
|
589
|
+
this.splice(update.start, update.items.length, ...update.deleted)
|
|
590
|
+
break
|
|
591
|
+
case `truncate`:
|
|
592
|
+
this.push(...update.items)
|
|
593
|
+
break
|
|
594
|
+
case `unshift`:
|
|
595
|
+
{
|
|
596
|
+
let i = update.items.length - 1
|
|
597
|
+
while (i >= 0) {
|
|
598
|
+
this.shift()
|
|
599
|
+
--i
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
break
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
public undo(update: PackedArrayUpdate<P>): number | null {
|
|
607
|
+
this.mode = `playback`
|
|
608
|
+
const unpacked = unpackArrayUpdate(update)
|
|
609
|
+
this.undoStep(unpacked)
|
|
610
|
+
this.mode = `record`
|
|
611
|
+
return null
|
|
612
|
+
}
|
|
613
|
+
}
|