@y/y 14.0.0-rc.2 → 14.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -12
- package/dist/src/index.d.ts +24 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/structs/AbstractStruct.d.ts +14 -17
- package/dist/src/structs/AbstractStruct.d.ts.map +1 -1
- package/dist/src/structs/GC.d.ts +9 -14
- package/dist/src/structs/GC.d.ts.map +1 -1
- package/dist/src/structs/Item.d.ts +569 -31
- package/dist/src/structs/Item.d.ts.map +1 -1
- package/dist/src/structs/Skip.d.ts +9 -11
- package/dist/src/structs/Skip.d.ts.map +1 -1
- package/dist/src/utils/BlockSet.d.ts +8 -7
- package/dist/src/utils/BlockSet.d.ts.map +1 -1
- package/dist/src/utils/Doc.d.ts +4 -9
- package/dist/src/utils/Doc.d.ts.map +1 -1
- package/dist/src/utils/ID.d.ts +0 -1
- package/dist/src/utils/ID.d.ts.map +1 -1
- package/dist/src/utils/RelativePosition.d.ts +2 -5
- package/dist/src/utils/RelativePosition.d.ts.map +1 -1
- package/dist/src/utils/Renderer.d.ts +144 -0
- package/dist/src/utils/Renderer.d.ts.map +1 -0
- package/dist/src/utils/Snapshot.d.ts +7 -11
- package/dist/src/utils/Snapshot.d.ts.map +1 -1
- package/dist/src/utils/StructStore.d.ts +45 -23
- package/dist/src/utils/StructStore.d.ts.map +1 -1
- package/dist/src/utils/Transaction.d.ts +11 -21
- package/dist/src/utils/Transaction.d.ts.map +1 -1
- package/dist/src/utils/UndoManager.d.ts +13 -14
- package/dist/src/utils/UndoManager.d.ts.map +1 -1
- package/dist/src/utils/UpdateDecoder.d.ts +1 -1
- package/dist/src/utils/UpdateDecoder.d.ts.map +1 -1
- package/dist/src/utils/UpdateEncoder.d.ts +0 -1
- package/dist/src/utils/UpdateEncoder.d.ts.map +1 -1
- package/dist/src/utils/YEvent.d.ts +22 -26
- package/dist/src/utils/YEvent.d.ts.map +1 -1
- package/dist/src/utils/content-helper.d.ts +2 -0
- package/dist/src/utils/content-helper.d.ts.map +1 -0
- package/dist/src/utils/delta-helpers.d.ts +2 -3
- package/dist/src/utils/delta-helpers.d.ts.map +1 -1
- package/dist/src/utils/encoding-helpers.d.ts +6 -0
- package/dist/src/utils/encoding-helpers.d.ts.map +1 -0
- package/dist/src/utils/encoding.d.ts +16 -18
- package/dist/src/utils/encoding.d.ts.map +1 -1
- package/dist/src/utils/ids.d.ts +331 -0
- package/dist/src/utils/ids.d.ts.map +1 -0
- package/dist/src/utils/isParentOf.d.ts +1 -2
- package/dist/src/utils/isParentOf.d.ts.map +1 -1
- package/dist/src/utils/logging.d.ts +0 -1
- package/dist/src/utils/logging.d.ts.map +1 -1
- package/dist/src/utils/meta.d.ts +15 -64
- package/dist/src/utils/meta.d.ts.map +1 -1
- package/dist/src/utils/renderer-helpers.d.ts +99 -0
- package/dist/src/utils/renderer-helpers.d.ts.map +1 -0
- package/dist/src/utils/schemas.d.ts +3 -0
- package/dist/src/utils/schemas.d.ts.map +1 -0
- package/dist/src/utils/transaction-helpers.d.ts +18 -0
- package/dist/src/utils/transaction-helpers.d.ts.map +1 -0
- package/dist/src/utils/updates.d.ts +19 -25
- package/dist/src/utils/updates.d.ts.map +1 -1
- package/dist/src/ytype.d.ts +144 -41
- package/dist/src/ytype.d.ts.map +1 -1
- package/global.d.ts +53 -0
- package/package.json +12 -16
- package/src/index.js +32 -124
- package/src/structs/AbstractStruct.js +21 -16
- package/src/structs/GC.js +15 -20
- package/src/structs/Item.js +992 -318
- package/src/structs/Skip.js +16 -19
- package/src/utils/BlockSet.js +20 -20
- package/src/utils/Doc.js +18 -29
- package/src/utils/ID.js +0 -2
- package/src/utils/RelativePosition.js +15 -25
- package/src/utils/{AttributionManager.js → Renderer.js} +42 -197
- package/src/utils/Snapshot.js +15 -37
- package/src/utils/StructStore.js +89 -227
- package/src/utils/Transaction.js +89 -321
- package/src/utils/UndoManager.js +157 -19
- package/src/utils/UpdateDecoder.js +2 -3
- package/src/utils/UpdateEncoder.js +0 -4
- package/src/utils/YEvent.js +20 -26
- package/src/utils/content-helper.js +0 -0
- package/src/utils/delta-helpers.js +21 -42
- package/src/utils/encoding-helpers.js +110 -0
- package/src/utils/encoding.js +197 -122
- package/src/utils/ids.js +1527 -0
- package/src/utils/isParentOf.js +2 -4
- package/src/utils/logging.js +0 -4
- package/src/utils/meta.js +57 -46
- package/src/utils/renderer-helpers.js +110 -0
- package/src/utils/schemas.js +3 -0
- package/src/utils/transaction-helpers.js +413 -0
- package/src/utils/updates.js +24 -146
- package/src/ytype.js +626 -255
- package/tests/testHelper.js +10 -12
- package/dist/src/internals.d.ts +0 -36
- package/dist/src/internals.d.ts.map +0 -1
- package/dist/src/structs/ContentAny.d.ts +0 -67
- package/dist/src/structs/ContentAny.d.ts.map +0 -1
- package/dist/src/structs/ContentBinary.d.ts +0 -64
- package/dist/src/structs/ContentBinary.d.ts.map +0 -1
- package/dist/src/structs/ContentDeleted.d.ts +0 -64
- package/dist/src/structs/ContentDeleted.d.ts.map +0 -1
- package/dist/src/structs/ContentDoc.d.ts +0 -72
- package/dist/src/structs/ContentDoc.d.ts.map +0 -1
- package/dist/src/structs/ContentEmbed.d.ts +0 -67
- package/dist/src/structs/ContentEmbed.d.ts.map +0 -1
- package/dist/src/structs/ContentFormat.d.ts +0 -69
- package/dist/src/structs/ContentFormat.d.ts.map +0 -1
- package/dist/src/structs/ContentJSON.d.ts +0 -70
- package/dist/src/structs/ContentJSON.d.ts.map +0 -1
- package/dist/src/structs/ContentString.d.ts +0 -70
- package/dist/src/structs/ContentString.d.ts.map +0 -1
- package/dist/src/structs/ContentType.d.ts +0 -77
- package/dist/src/structs/ContentType.d.ts.map +0 -1
- package/dist/src/utils/AttributionManager.d.ts +0 -238
- package/dist/src/utils/AttributionManager.d.ts.map +0 -1
- package/dist/src/utils/IdMap.d.ts +0 -164
- package/dist/src/utils/IdMap.d.ts.map +0 -1
- package/dist/src/utils/IdSet.d.ts +0 -163
- package/dist/src/utils/IdSet.d.ts.map +0 -1
- package/dist/tests/IdMap.tests.d.ts +0 -9
- package/dist/tests/IdMap.tests.d.ts.map +0 -1
- package/dist/tests/IdSet.tests.d.ts +0 -9
- package/dist/tests/IdSet.tests.d.ts.map +0 -1
- package/dist/tests/attribution.tests.d.ts +0 -9
- package/dist/tests/attribution.tests.d.ts.map +0 -1
- package/dist/tests/compatibility.tests.d.ts +0 -5
- package/dist/tests/compatibility.tests.d.ts.map +0 -1
- package/dist/tests/delta.tests.d.ts +0 -7
- package/dist/tests/delta.tests.d.ts.map +0 -1
- package/dist/tests/doc.tests.d.ts +0 -13
- package/dist/tests/doc.tests.d.ts.map +0 -1
- package/dist/tests/encoding.tests.d.ts +0 -5
- package/dist/tests/encoding.tests.d.ts.map +0 -1
- package/dist/tests/index.d.ts +0 -2
- package/dist/tests/index.d.ts.map +0 -1
- package/dist/tests/relativePositions.tests.d.ts +0 -11
- package/dist/tests/relativePositions.tests.d.ts.map +0 -1
- package/dist/tests/snapshot.tests.d.ts +0 -14
- package/dist/tests/snapshot.tests.d.ts.map +0 -1
- package/dist/tests/testHelper.d.ts +0 -171
- package/dist/tests/testHelper.d.ts.map +0 -1
- package/dist/tests/undo-redo.tests.d.ts +0 -27
- package/dist/tests/undo-redo.tests.d.ts.map +0 -1
- package/dist/tests/updates.tests.d.ts +0 -26
- package/dist/tests/updates.tests.d.ts.map +0 -1
- package/dist/tests/y-array.tests.d.ts +0 -43
- package/dist/tests/y-array.tests.d.ts.map +0 -1
- package/dist/tests/y-map.tests.d.ts +0 -42
- package/dist/tests/y-map.tests.d.ts.map +0 -1
- package/dist/tests/y-text.tests.d.ts +0 -49
- package/dist/tests/y-text.tests.d.ts.map +0 -1
- package/dist/tests/y-xml.tests.d.ts +0 -14
- package/dist/tests/y-xml.tests.d.ts.map +0 -1
- package/src/internals.js +0 -35
- package/src/structs/ContentAny.js +0 -115
- package/src/structs/ContentBinary.js +0 -93
- package/src/structs/ContentDeleted.js +0 -101
- package/src/structs/ContentDoc.js +0 -141
- package/src/structs/ContentEmbed.js +0 -98
- package/src/structs/ContentFormat.js +0 -105
- package/src/structs/ContentJSON.js +0 -119
- package/src/structs/ContentString.js +0 -113
- package/src/structs/ContentType.js +0 -152
- package/src/utils/IdMap.js +0 -673
- package/src/utils/IdSet.js +0 -825
package/src/utils/Transaction.js
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getState,
|
|
3
|
-
writeStructsFromTransaction,
|
|
4
|
-
writeIdSet,
|
|
5
|
-
getStateVector,
|
|
6
|
-
findIndexSS,
|
|
7
|
-
callEventHandlerListeners,
|
|
8
|
-
createIdSet,
|
|
9
|
-
Item,
|
|
10
|
-
generateNewClientId,
|
|
11
|
-
createID,
|
|
12
|
-
iterateStructsByIdSet,
|
|
13
|
-
ContentFormat,
|
|
14
|
-
IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractStruct, YEvent, Doc // eslint-disable-line
|
|
15
|
-
} from '../internals.js'
|
|
16
|
-
|
|
17
|
-
import { YType } from '../ytype.js' // eslint-disable-line
|
|
18
1
|
import * as error from 'lib0/error'
|
|
19
2
|
import * as map from 'lib0/map'
|
|
20
3
|
import * as math from 'lib0/math'
|
|
21
|
-
import * as set from 'lib0/set'
|
|
22
4
|
import * as logging from 'lib0/logging'
|
|
23
5
|
import { callAll } from 'lib0/function'
|
|
24
6
|
|
|
7
|
+
import { ContentFormat } from '../structs/Item.js'
|
|
8
|
+
import { getStateVector } from './StructStore.js'
|
|
9
|
+
import { callEventHandlerListeners } from './EventHandler.js'
|
|
10
|
+
import { createIdSet, iterateStructsByIdSet } from './ids.js'
|
|
11
|
+
import { GC } from '../structs/GC.js'
|
|
12
|
+
import { YEvent } from './YEvent.js'
|
|
13
|
+
import { writeUpdateMessageFromTransaction } from './encoding-helpers.js'
|
|
14
|
+
import { UpdateEncoderV1, UpdateEncoderV2 } from './UpdateEncoder.js'
|
|
15
|
+
import { findIndexSS, updateCurrentFormats, cleanupFormattingGap, tryGcDeleteSet, tryMerge, tryToMergeWithLefts, cleanupContextlessFormattingGap } from './transaction-helpers.js'
|
|
16
|
+
import * as random from 'lib0/random'
|
|
17
|
+
|
|
18
|
+
export const generateNewClientId = random.uint53
|
|
19
|
+
|
|
25
20
|
/**
|
|
26
21
|
* A transaction is created for every change on the Yjs model. It is possible
|
|
27
22
|
* to bundle changes on the Yjs model in a single transaction to
|
|
@@ -31,18 +26,18 @@ import { callAll } from 'lib0/function'
|
|
|
31
26
|
*
|
|
32
27
|
* @example
|
|
33
28
|
* const ydoc = new Y.Doc()
|
|
34
|
-
* const map = ydoc.
|
|
29
|
+
* const map = ydoc.get('map')
|
|
35
30
|
* // Log content when change is triggered
|
|
36
31
|
* map.observe(() => {
|
|
37
32
|
* console.log('change triggered')
|
|
38
33
|
* })
|
|
39
34
|
* // Each change on the map type triggers a log message:
|
|
40
|
-
* map.
|
|
41
|
-
* map.
|
|
35
|
+
* map.setAttr('a', 0) // => "change triggered"
|
|
36
|
+
* map.setAttr('b', 0) // => "change triggered"
|
|
42
37
|
* // When put in a transaction, it will trigger the log after the transaction:
|
|
43
38
|
* ydoc.transact(() => {
|
|
44
|
-
* map.
|
|
45
|
-
* map.
|
|
39
|
+
* map.setAttr('a', 1)
|
|
40
|
+
* map.setAttr('b', 1)
|
|
46
41
|
* }) // => "change triggered"
|
|
47
42
|
*
|
|
48
43
|
* @public
|
|
@@ -170,257 +165,17 @@ export class Transaction {
|
|
|
170
165
|
}
|
|
171
166
|
}
|
|
172
167
|
|
|
173
|
-
/**
|
|
174
|
-
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
175
|
-
* @param {Transaction} transaction
|
|
176
|
-
* @return {boolean} Whether data was written.
|
|
177
|
-
*/
|
|
178
|
-
export const writeUpdateMessageFromTransaction = (encoder, transaction) => {
|
|
179
|
-
if (transaction.deleteSet.clients.size === 0 && transaction.insertSet.clients.size === 0) {
|
|
180
|
-
return false
|
|
181
|
-
}
|
|
182
|
-
writeStructsFromTransaction(encoder, transaction)
|
|
183
|
-
writeIdSet(encoder, transaction.deleteSet)
|
|
184
|
-
return true
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* @param {Transaction} transaction
|
|
189
|
-
*
|
|
190
|
-
* @private
|
|
191
|
-
* @function
|
|
192
|
-
*/
|
|
193
|
-
export const nextID = transaction => {
|
|
194
|
-
const y = transaction.doc
|
|
195
|
-
return createID(y.clientID, getState(y.store, y.clientID))
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* If `type.parent` was added in current transaction, `type` technically
|
|
200
|
-
* did not change, it was just added and we should not fire events for `type`.
|
|
201
|
-
*
|
|
202
|
-
* @param {Transaction} transaction
|
|
203
|
-
* @param {YType} type
|
|
204
|
-
* @param {string|null} parentSub
|
|
205
|
-
*/
|
|
206
|
-
export const addChangedTypeToTransaction = (transaction, type, parentSub) => {
|
|
207
|
-
const item = type._item
|
|
208
|
-
if (item === null || (!item.deleted && !transaction.insertSet.hasId(item.id))) {
|
|
209
|
-
map.setIfUndefined(transaction.changed, type, set.create).add(parentSub)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @param {Array<AbstractStruct>} structs
|
|
215
|
-
* @param {number} pos
|
|
216
|
-
* @return {number} # of merged structs
|
|
217
|
-
*/
|
|
218
|
-
const tryToMergeWithLefts = (structs, pos) => {
|
|
219
|
-
let right = structs[pos]
|
|
220
|
-
let left = structs[pos - 1]
|
|
221
|
-
let i = pos
|
|
222
|
-
for (; i > 0; right = left, left = structs[--i - 1]) {
|
|
223
|
-
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
|
224
|
-
if (left.mergeWith(right)) {
|
|
225
|
-
if (right instanceof Item && right.parentSub !== null && /** @type {YType} */ (right.parent)._map.get(right.parentSub) === right) {
|
|
226
|
-
/** @type {YType} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left))
|
|
227
|
-
}
|
|
228
|
-
continue
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
break
|
|
232
|
-
}
|
|
233
|
-
const merged = pos - i
|
|
234
|
-
if (merged) {
|
|
235
|
-
// remove all merged structs from the array
|
|
236
|
-
structs.splice(pos + 1 - merged, merged)
|
|
237
|
-
}
|
|
238
|
-
return merged
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* @param {Transaction} tr
|
|
243
|
-
* @param {IdSet} ds
|
|
244
|
-
* @param {function(Item):boolean} gcFilter
|
|
245
|
-
*/
|
|
246
|
-
const tryGcDeleteSet = (tr, ds, gcFilter) => {
|
|
247
|
-
for (const [client, _deleteItems] of ds.clients.entries()) {
|
|
248
|
-
const deleteItems = _deleteItems.getIds()
|
|
249
|
-
const structs = /** @type {Array<GC|Item>} */ (tr.doc.store.clients.get(client))
|
|
250
|
-
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
251
|
-
const deleteItem = deleteItems[di]
|
|
252
|
-
const endDeleteItemClock = deleteItem.clock + deleteItem.len
|
|
253
|
-
for (
|
|
254
|
-
let si = findIndexSS(structs, deleteItem.clock), struct = structs[si];
|
|
255
|
-
si < structs.length && struct.id.clock < endDeleteItemClock;
|
|
256
|
-
struct = structs[++si]
|
|
257
|
-
) {
|
|
258
|
-
const struct = structs[si]
|
|
259
|
-
if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
|
|
260
|
-
break
|
|
261
|
-
}
|
|
262
|
-
if (struct instanceof Item && struct.deleted && !struct.keep && gcFilter(struct)) {
|
|
263
|
-
struct.gc(tr, false)
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* @param {IdSet} ds
|
|
272
|
-
* @param {StructStore} store
|
|
273
|
-
*/
|
|
274
|
-
const tryMerge = (ds, store) => {
|
|
275
|
-
// try to merge deleted / gc'd items
|
|
276
|
-
// merge from right to left for better efficiency and so we don't miss any merge targets
|
|
277
|
-
ds.clients.forEach((_deleteItems, client) => {
|
|
278
|
-
const deleteItems = _deleteItems.getIds()
|
|
279
|
-
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
|
280
|
-
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
281
|
-
const deleteItem = deleteItems[di]
|
|
282
|
-
// start with merging the item next to the last deleted item
|
|
283
|
-
const mostRightIndexToCheck = math.min(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1))
|
|
284
|
-
for (
|
|
285
|
-
let si = mostRightIndexToCheck, struct = structs[si];
|
|
286
|
-
si > 0 && struct.id.clock >= deleteItem.clock;
|
|
287
|
-
struct = structs[si]
|
|
288
|
-
) {
|
|
289
|
-
si -= 1 + tryToMergeWithLefts(structs, si)
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* @param {Transaction} tr
|
|
297
|
-
* @param {IdSet} idset
|
|
298
|
-
* @param {function(Item):boolean} gcFilter
|
|
299
|
-
*/
|
|
300
|
-
export const tryGc = (tr, idset, gcFilter) => {
|
|
301
|
-
tryGcDeleteSet(tr, idset, gcFilter)
|
|
302
|
-
tryMerge(idset, tr.doc.store)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* @param {Transaction} transaction
|
|
307
|
-
* @param {Item | null} item
|
|
308
|
-
*/
|
|
309
|
-
const cleanupContextlessFormattingGap = (transaction, item) => {
|
|
310
|
-
if (!transaction.doc.cleanupFormatting) return 0
|
|
311
|
-
// iterate until item.right is null or content
|
|
312
|
-
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
|
313
|
-
item = item.right
|
|
314
|
-
}
|
|
315
|
-
const attrs = new Set()
|
|
316
|
-
// iterate back until a content item is found
|
|
317
|
-
while (item && (item.deleted || !item.countable)) {
|
|
318
|
-
if (!item.deleted && item.content.constructor === ContentFormat) {
|
|
319
|
-
const key = /** @type {ContentFormat} */ (item.content).key
|
|
320
|
-
if (attrs.has(key)) {
|
|
321
|
-
item.delete(transaction)
|
|
322
|
-
transaction.cleanUps.add(item.id.client, item.id.clock, item.length)
|
|
323
|
-
} else {
|
|
324
|
-
attrs.add(key)
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
item = item.left
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* @param {Map<string,any>} currentAttributes
|
|
333
|
-
* @param {ContentFormat} format
|
|
334
|
-
*
|
|
335
|
-
* @private
|
|
336
|
-
* @function
|
|
337
|
-
*/
|
|
338
|
-
const updateCurrentAttributes = (currentAttributes, { key, value }) => {
|
|
339
|
-
if (value === null) {
|
|
340
|
-
currentAttributes.delete(key)
|
|
341
|
-
} else {
|
|
342
|
-
currentAttributes.set(key, value)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Call this function after string content has been deleted in order to
|
|
348
|
-
* clean up formatting Items.
|
|
349
|
-
*
|
|
350
|
-
* @param {Transaction} transaction
|
|
351
|
-
* @param {Item} start
|
|
352
|
-
* @param {Item|null} curr exclusive end, automatically iterates to the next Content Item
|
|
353
|
-
* @param {Map<string,any>} startAttributes
|
|
354
|
-
* @param {Map<string,any>} currAttributes
|
|
355
|
-
* @return {number} The amount of formatting Items deleted.
|
|
356
|
-
*
|
|
357
|
-
* @function
|
|
358
|
-
*/
|
|
359
|
-
export const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
|
360
|
-
if (!transaction.doc.cleanupFormatting) return 0
|
|
361
|
-
/**
|
|
362
|
-
* @type {Item|null}
|
|
363
|
-
*/
|
|
364
|
-
let end = start
|
|
365
|
-
/**
|
|
366
|
-
* @type {Map<string,ContentFormat>}
|
|
367
|
-
*/
|
|
368
|
-
const endFormats = map.create()
|
|
369
|
-
while (end && (!end.countable || end.deleted)) {
|
|
370
|
-
if (!end.deleted && end.content.constructor === ContentFormat) {
|
|
371
|
-
const cf = /** @type {ContentFormat} */ (end.content)
|
|
372
|
-
endFormats.set(cf.key, cf)
|
|
373
|
-
}
|
|
374
|
-
end = end.right
|
|
375
|
-
}
|
|
376
|
-
let cleanups = 0
|
|
377
|
-
let reachedCurr = false
|
|
378
|
-
while (start !== end) {
|
|
379
|
-
if (curr === start) {
|
|
380
|
-
reachedCurr = true
|
|
381
|
-
}
|
|
382
|
-
if (!start.deleted) {
|
|
383
|
-
const content = start.content
|
|
384
|
-
switch (content.constructor) {
|
|
385
|
-
case ContentFormat: {
|
|
386
|
-
const { key, value } = /** @type {ContentFormat} */ (content)
|
|
387
|
-
const startAttrValue = startAttributes.get(key) ?? null
|
|
388
|
-
if (endFormats.get(key) !== content || startAttrValue === value) {
|
|
389
|
-
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
|
390
|
-
start.delete(transaction)
|
|
391
|
-
transaction.cleanUps.add(start.id.client, start.id.clock, start.length)
|
|
392
|
-
cleanups++
|
|
393
|
-
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
|
394
|
-
if (startAttrValue === null) {
|
|
395
|
-
currAttributes.delete(key)
|
|
396
|
-
} else {
|
|
397
|
-
currAttributes.set(key, startAttrValue)
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if (!reachedCurr && !start.deleted) {
|
|
402
|
-
updateCurrentAttributes(currAttributes, /** @type {ContentFormat} */ (content))
|
|
403
|
-
}
|
|
404
|
-
break
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
start = /** @type {Item} */ (start.right)
|
|
409
|
-
}
|
|
410
|
-
return cleanups
|
|
411
|
-
}
|
|
412
|
-
|
|
413
168
|
/**
|
|
414
169
|
* This function is experimental and subject to change / be removed.
|
|
415
170
|
*
|
|
416
|
-
* Ideally, we don't need this function at all.
|
|
171
|
+
* Ideally, we don't need this function at all. Formats should be cleaned up
|
|
417
172
|
* automatically after each change. This function iterates twice over the complete YText type
|
|
418
|
-
* and removes unnecessary
|
|
173
|
+
* and removes unnecessary formats. This is also helpful for testing.
|
|
419
174
|
*
|
|
420
175
|
* This function won't be exported anymore as soon as there is confidence that the YText type works as intended.
|
|
421
176
|
*
|
|
422
177
|
* @param {YType} type
|
|
423
|
-
* @return {number} How many
|
|
178
|
+
* @return {number} How many formats have been cleaned up.
|
|
424
179
|
*/
|
|
425
180
|
export const cleanupYTextFormatting = type => {
|
|
426
181
|
if (!type.doc?.cleanupFormatting) return 0
|
|
@@ -428,17 +183,17 @@ export const cleanupYTextFormatting = type => {
|
|
|
428
183
|
transact(/** @type {Doc} */ (type.doc), transaction => {
|
|
429
184
|
let start = /** @type {Item} */ (type._start)
|
|
430
185
|
let end = type._start
|
|
431
|
-
let
|
|
432
|
-
const
|
|
186
|
+
let startFormats = map.create()
|
|
187
|
+
const currentFormats = map.copy(startFormats)
|
|
433
188
|
while (end) {
|
|
434
189
|
if (end.deleted === false) {
|
|
435
190
|
switch (end.content.constructor) {
|
|
436
191
|
case ContentFormat:
|
|
437
|
-
|
|
192
|
+
updateCurrentFormats(currentFormats, /** @type {ContentFormat} */ (end.content))
|
|
438
193
|
break
|
|
439
194
|
default:
|
|
440
|
-
res += cleanupFormattingGap(transaction, start, end,
|
|
441
|
-
|
|
195
|
+
res += cleanupFormattingGap(transaction, start, end, startFormats, currentFormats)
|
|
196
|
+
startFormats = map.copy(currentFormats)
|
|
442
197
|
start = end
|
|
443
198
|
break
|
|
444
199
|
}
|
|
@@ -449,50 +204,6 @@ export const cleanupYTextFormatting = type => {
|
|
|
449
204
|
return res
|
|
450
205
|
}
|
|
451
206
|
|
|
452
|
-
/**
|
|
453
|
-
* This will be called by the transaction once the event handlers are called to potentially cleanup
|
|
454
|
-
* formatting attributes.
|
|
455
|
-
*
|
|
456
|
-
* @param {Transaction} transaction
|
|
457
|
-
*/
|
|
458
|
-
export const cleanupYTextAfterTransaction = transaction => {
|
|
459
|
-
/**
|
|
460
|
-
* @type {Set<YType>}
|
|
461
|
-
*/
|
|
462
|
-
const needFullCleanup = new Set()
|
|
463
|
-
// check if another formatting item was inserted
|
|
464
|
-
const doc = transaction.doc
|
|
465
|
-
iterateStructsByIdSet(transaction, transaction.insertSet, (item) => {
|
|
466
|
-
if (
|
|
467
|
-
!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC
|
|
468
|
-
) {
|
|
469
|
-
needFullCleanup.add(/** @type {any} */ (item).parent)
|
|
470
|
-
}
|
|
471
|
-
})
|
|
472
|
-
// cleanup in a new transaction
|
|
473
|
-
transact(doc, (t) => {
|
|
474
|
-
iterateStructsByIdSet(transaction, transaction.deleteSet, item => {
|
|
475
|
-
if (item instanceof GC || !(/** @type {YType} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YType} */ (item.parent))) {
|
|
476
|
-
return
|
|
477
|
-
}
|
|
478
|
-
const parent = /** @type {YType} */ (item.parent)
|
|
479
|
-
if (item.content.constructor === ContentFormat) {
|
|
480
|
-
needFullCleanup.add(parent)
|
|
481
|
-
} else {
|
|
482
|
-
// If no formatting attribute was inserted or deleted, we can make due with contextless
|
|
483
|
-
// formatting cleanups.
|
|
484
|
-
// Contextless: it is not necessary to compute currentAttributes for the affected position.
|
|
485
|
-
cleanupContextlessFormattingGap(t, item)
|
|
486
|
-
}
|
|
487
|
-
})
|
|
488
|
-
// If a formatting item was inserted, we simply clean the whole type.
|
|
489
|
-
// We need to compute currentAttributes for the current position anyway.
|
|
490
|
-
for (const yText of needFullCleanup) {
|
|
491
|
-
cleanupYTextFormatting(yText)
|
|
492
|
-
}
|
|
493
|
-
})
|
|
494
|
-
}
|
|
495
|
-
|
|
496
207
|
/**
|
|
497
208
|
* @param {Array<Transaction>} transactionCleanups
|
|
498
209
|
* @param {number} i
|
|
@@ -525,17 +236,29 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|
|
525
236
|
})
|
|
526
237
|
)
|
|
527
238
|
fs.push(() => {
|
|
528
|
-
// deep observe events
|
|
239
|
+
// deep observe events + the RDT `'delta'` channel. `changedParentTypes` holds the changed
|
|
240
|
+
// type AND all of its ancestors, so both `observeDeep` and `'delta'` bubble identically.
|
|
529
241
|
transaction.changedParentTypes.forEach((events, type) => {
|
|
530
242
|
// We need to think about the possibility that the user transforms the
|
|
531
243
|
// Y.Doc in the event.
|
|
532
|
-
if (type.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
244
|
+
if (type._item !== null && type._item.deleted) return
|
|
245
|
+
const hasDeep = type._dEH.l.length > 0
|
|
246
|
+
const hasDeltaListeners = (type._observers.get('delta')?.size ?? 0) > 0
|
|
247
|
+
const maintaining = type._delta !== null
|
|
248
|
+
if (!hasDeep && !hasDeltaListeners && !maintaining) return
|
|
249
|
+
/**
|
|
250
|
+
* @type {YEvent<any>}
|
|
251
|
+
*/
|
|
252
|
+
const deepEventHandler = events.find(event => event.target === type) || new YEvent(type, transaction, new Set(null))
|
|
253
|
+
if (hasDeep) {
|
|
537
254
|
callEventHandlerListeners(type._dEH, deepEventHandler, transaction)
|
|
538
255
|
}
|
|
256
|
+
if (hasDeltaListeners || maintaining) {
|
|
257
|
+
// the type-rooted deep delta of this transaction (a nested `modify` chain for ancestors)
|
|
258
|
+
const change = /** @type {any} */ (deepEventHandler.getDelta({ renderer: type._renderer, deep: true }).done())
|
|
259
|
+
type._delta?.apply(change) // keep the cache current (incl. ancestors and diff-renderer attributions)
|
|
260
|
+
if (hasDeltaListeners) type.emit('delta', [change])
|
|
261
|
+
}
|
|
539
262
|
})
|
|
540
263
|
})
|
|
541
264
|
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
|
@@ -621,6 +344,50 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|
|
621
344
|
}
|
|
622
345
|
}
|
|
623
346
|
|
|
347
|
+
/**
|
|
348
|
+
* This will be called by the transaction once the event handlers are called to potentially cleanup
|
|
349
|
+
* formats.
|
|
350
|
+
*
|
|
351
|
+
* @param {Transaction} transaction
|
|
352
|
+
*/
|
|
353
|
+
export const cleanupYTextAfterTransaction = transaction => {
|
|
354
|
+
/**
|
|
355
|
+
* @type {Set<YType>}
|
|
356
|
+
*/
|
|
357
|
+
const needFullCleanup = new Set()
|
|
358
|
+
// check if another formatting item was inserted
|
|
359
|
+
const doc = transaction.doc
|
|
360
|
+
iterateStructsByIdSet(transaction, transaction.insertSet, (item) => {
|
|
361
|
+
if (
|
|
362
|
+
!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC
|
|
363
|
+
) {
|
|
364
|
+
needFullCleanup.add(/** @type {any} */ (item).parent)
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
// cleanup in a new transaction
|
|
368
|
+
transact(doc, (t) => {
|
|
369
|
+
iterateStructsByIdSet(transaction, transaction.deleteSet, item => {
|
|
370
|
+
if (item instanceof GC || !(/** @type {YType} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YType} */ (item.parent))) {
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
const parent = /** @type {YType} */ (item.parent)
|
|
374
|
+
if (item.content.constructor === ContentFormat) {
|
|
375
|
+
needFullCleanup.add(parent)
|
|
376
|
+
} else {
|
|
377
|
+
// If no format was inserted or deleted, we can make due with contextless
|
|
378
|
+
// formatting cleanups.
|
|
379
|
+
// Contextless: it is not necessary to compute currentFormats for the affected position.
|
|
380
|
+
cleanupContextlessFormattingGap(t, item)
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
// If a formatting item was inserted, we simply clean the whole type.
|
|
384
|
+
// We need to compute currentFormats for the current position anyway.
|
|
385
|
+
for (const yText of needFullCleanup) {
|
|
386
|
+
cleanupYTextFormatting(yText)
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
|
|
624
391
|
/**
|
|
625
392
|
* Implements the functionality of `y.transact(()=>{..})`
|
|
626
393
|
*
|
|
@@ -628,6 +395,7 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|
|
628
395
|
* @param {Doc} doc
|
|
629
396
|
* @param {function(Transaction):T} f
|
|
630
397
|
* @param {any} [origin=true]
|
|
398
|
+
* @param {boolean} [local=true]
|
|
631
399
|
* @return {T}
|
|
632
400
|
*
|
|
633
401
|
* @function
|