lib0 1.0.0-rc.30 → 1.0.0-rc.32
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/delta/delta.d.ts
CHANGED
|
@@ -85,6 +85,10 @@ export const $deltaMapChangeJson: s.Schema<DeltaAttrOpJSON>;
|
|
|
85
85
|
* fingerprint read (and any `diff` / equality check that relies on it) is
|
|
86
86
|
* wrong. Fields covered: insert, delete, retain, format, attribution,
|
|
87
87
|
* value, key.
|
|
88
|
+
* - **A `clone` copies `_fingerprint` when the copy is content-identical:** a full range and the
|
|
89
|
+
* same key. Freezing a nested value with `done()` is fingerprint-neutral (trailing plain retains
|
|
90
|
+
* are not fingerprinted), so nothing else can differ. The root delta returned by
|
|
91
|
+
* `clone`/`slice`/`cloneDeep` never inherits a fingerprint.
|
|
88
92
|
*
|
|
89
93
|
* @internal not part of the consumer API — a content op; build deltas via {@link create}/{@link DeltaBuilder}.
|
|
90
94
|
*/
|
|
@@ -433,9 +437,12 @@ export class SetAttrOp<V extends unknown = any, K extends string | number = any>
|
|
|
433
437
|
* Full (frozen) clone. A `move` apply reuses the source op instead of cloning, so there is no
|
|
434
438
|
* shared-mutable variant — see {@link DeltaBuilder#apply}.
|
|
435
439
|
*
|
|
436
|
-
* @
|
|
440
|
+
* @template {string|number} [K2=K]
|
|
441
|
+
* @param {K2} [key] retarget the clone to another attribute key (a transformer projecting or
|
|
442
|
+
* renaming an attribute); defaults to this op's key.
|
|
443
|
+
* @return {SetAttrOp<V,K2>}
|
|
437
444
|
*/
|
|
438
|
-
clone(): SetAttrOp<V,
|
|
445
|
+
clone<K2 extends string | number = K>(key?: K2): SetAttrOp<V, K2>;
|
|
439
446
|
$type: s.Schema<SetAttrOp<any, any>>;
|
|
440
447
|
/**
|
|
441
448
|
* @param {SetAttrOp<V>} other
|
|
@@ -475,9 +482,11 @@ export class DeleteAttrOp<V = any, K extends string | number = string | number>
|
|
|
475
482
|
/**
|
|
476
483
|
* Full (frozen) clone; a `move` apply reuses the source op instead — see {@link DeltaBuilder#apply}.
|
|
477
484
|
*
|
|
478
|
-
* @
|
|
485
|
+
* @template {string|number} [K2=K]
|
|
486
|
+
* @param {K2} [key] retarget the clone to another attribute key; defaults to this op's key.
|
|
487
|
+
* @return {DeleteAttrOp<V,K2>}
|
|
479
488
|
*/
|
|
480
|
-
clone(): DeleteAttrOp<V,
|
|
489
|
+
clone<K2 extends string | number = K>(key?: K2): DeleteAttrOp<V, K2>;
|
|
481
490
|
$type: s.Schema<DeleteAttrOp<any, string | number>>;
|
|
482
491
|
/**
|
|
483
492
|
* @param {DeleteAttrOp<V>} other
|
|
@@ -531,9 +540,11 @@ export class ModifyAttrOp<Modifier extends DeltaAny = DeltaAny, K extends string
|
|
|
531
540
|
/**
|
|
532
541
|
* Full (frozen) clone; a `move` apply reuses the source op instead — see {@link DeltaBuilder#apply}.
|
|
533
542
|
*
|
|
534
|
-
* @
|
|
543
|
+
* @template {string|number} [K2=K]
|
|
544
|
+
* @param {K2} [key] retarget the clone to another attribute key; defaults to this op's key.
|
|
545
|
+
* @return {ModifyAttrOp<Modifier,K2>}
|
|
535
546
|
*/
|
|
536
|
-
clone(): ModifyAttrOp<Modifier,
|
|
547
|
+
clone<K2 extends string | number = K>(key?: K2): ModifyAttrOp<Modifier, K2>;
|
|
537
548
|
$type: s.Schema<ModifyAttrOp<any, string>>;
|
|
538
549
|
/**
|
|
539
550
|
* @param {ModifyAttrOp<Modifier>} other
|
|
@@ -545,7 +556,7 @@ export const $modifyOp: s.Schema<ModifyOp<Delta<any>>>;
|
|
|
545
556
|
export const $textOp: s.Schema<TextOp>;
|
|
546
557
|
export const $deleteOp: s.Schema<DeleteOp<any>>;
|
|
547
558
|
export const $retainOp: s.Schema<RetainOp>;
|
|
548
|
-
export const $anyOp: s.Schema<
|
|
559
|
+
export const $anyOp: s.Schema<TextOp | InsertOp<any> | DeleteOp<any> | ModifyOp<Delta<any>>>;
|
|
549
560
|
export const $setAttrOp: s.Schema<SetAttrOp<any>>;
|
|
550
561
|
export const $modifyAttrOp: s.Schema<ModifyAttrOp<any>>;
|
|
551
562
|
export const $deleteAttrOp: s.Schema<DeleteAttrOp<any>>;
|
|
@@ -1263,9 +1274,20 @@ export type DeltaConfigGetRecursiveAttrs<Conf_1 extends DeltaConf> = Conf_1 exte
|
|
|
1263
1274
|
recursiveAttrs: true;
|
|
1264
1275
|
} ? true : false;
|
|
1265
1276
|
/**
|
|
1266
|
-
* Transform Delta(Builder) to a normal delta
|
|
1267
|
-
|
|
1268
|
-
|
|
1277
|
+
* Transform Delta(Builder) to a normal delta: a `DeltaBuilder<Conf>` becomes `Delta<Conf>`, anything
|
|
1278
|
+
* else passes through.
|
|
1279
|
+
*
|
|
1280
|
+
* Infers the conf from `DeltaBuilder` on purpose, not from `Delta<infer Conf>`. lib0's own source
|
|
1281
|
+
* check never had a problem with the latter, but a consumer type-checks the emitted declaration
|
|
1282
|
+
* files, and there matching a builder against `Delta<infer Conf>` is structural: TypeScript walks
|
|
1283
|
+
* `Delta`'s members and recurses through {@link DeltaConfGetChildren}'s recursive branch until its
|
|
1284
|
+
* instantiation-depth limit, so every nested condensed builder (for example
|
|
1285
|
+
* `create().insert([create('c', {}, [create('p', {}, 'x')])])`) failed with TS2589 in
|
|
1286
|
+
* y-prosemirror and yjs. The structural walk also leaked the grandchild conf into the child
|
|
1287
|
+
* union; inferring from the builder keeps the nesting exact. dist-check/consumer.js pins both
|
|
1288
|
+
* against dist.
|
|
1289
|
+
*/
|
|
1290
|
+
export type _SanifyDelta<V> = V extends never ? never : (import("../ts.js").TypeIsAny<V, any, V extends DeltaBuilder<infer Conf_1, any> ? Delta<Conf_1> : V>);
|
|
1269
1291
|
export type PrettifyDeltaConf<Conf_1 extends DeltaConf> = import("../ts.js").Prettify<{ [K in keyof Conf_1]: K extends "attrs" ? import("../ts.js").Prettify<{ [KA in keyof Conf_1[K]]: _SanifyDelta<Conf_1[K][KA]>; }, 1> : (K extends "children" ? _SanifyDelta<Conf_1[K]> : Conf_1[K]); }, 1>;
|
|
1270
1292
|
export type DeltaConfOverwrite<D1 extends DeltaConf, D2> = (import("../ts.js").TypeIsAny<D1, any, PrettifyDeltaConf<{ [K in (keyof D1 | keyof D2)]: K extends keyof D2 ? D2[K] : (K extends keyof D1 ? D1[K] : never); }>> & {}) extends infer DC extends DeltaConf ? DC : never;
|
|
1271
1293
|
export type ReadableDeltaConf = {
|
|
@@ -1512,10 +1534,21 @@ declare class Mark {
|
|
|
1512
1534
|
* @typedef {Conf extends {recursiveAttrs:true} ? true : false} DeltaConfigGetRecursiveAttrs
|
|
1513
1535
|
*/
|
|
1514
1536
|
/**
|
|
1515
|
-
* Transform Delta(Builder) to a normal delta
|
|
1537
|
+
* Transform Delta(Builder) to a normal delta: a `DeltaBuilder<Conf>` becomes `Delta<Conf>`, anything
|
|
1538
|
+
* else passes through.
|
|
1539
|
+
*
|
|
1540
|
+
* Infers the conf from `DeltaBuilder` on purpose, not from `Delta<infer Conf>`. lib0's own source
|
|
1541
|
+
* check never had a problem with the latter, but a consumer type-checks the emitted declaration
|
|
1542
|
+
* files, and there matching a builder against `Delta<infer Conf>` is structural: TypeScript walks
|
|
1543
|
+
* `Delta`'s members and recurses through {@link DeltaConfGetChildren}'s recursive branch until its
|
|
1544
|
+
* instantiation-depth limit, so every nested condensed builder (for example
|
|
1545
|
+
* `create().insert([create('c', {}, [create('p', {}, 'x')])])`) failed with TS2589 in
|
|
1546
|
+
* y-prosemirror and yjs. The structural walk also leaked the grandchild conf into the child
|
|
1547
|
+
* union; inferring from the builder keeps the nesting exact. dist-check/consumer.js pins both
|
|
1548
|
+
* against dist.
|
|
1516
1549
|
*
|
|
1517
1550
|
* @template V
|
|
1518
|
-
* @typedef {V extends never ? never : (import('../ts.js').TypeIsAny<V,any,V extends
|
|
1551
|
+
* @typedef {V extends never ? never : (import('../ts.js').TypeIsAny<V,any,V extends DeltaBuilder<infer Conf, any> ? Delta<Conf> : V>)} _SanifyDelta
|
|
1519
1552
|
*/
|
|
1520
1553
|
/**
|
|
1521
1554
|
* @template {DeltaConf} Conf
|
package/package.json
CHANGED
package/src/delta/delta.js
CHANGED
|
@@ -142,6 +142,29 @@ export const $deltaMapChangeJson = /* @__PURE__ */(() => s.$union(
|
|
|
142
142
|
* @return {Attrs}
|
|
143
143
|
*/
|
|
144
144
|
const _cloneAttrs = attrs => attrs == null ? attrs : { ...attrs }
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A retain with neither format nor attribution — positional only, not content. `done()` trims a
|
|
148
|
+
* trailing run of them; fingerprint, equality and `isEmpty` ignore that run. (`format: null` is a
|
|
149
|
+
* clear instruction and therefore content — the same rule `done()` applies.)
|
|
150
|
+
*
|
|
151
|
+
* @param {ChildrenOpAny} op
|
|
152
|
+
*/
|
|
153
|
+
const _isPlainRetain = op => $retainOp.check(op) && op.format === undefined && op.attribution === undefined
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The number of content ops in a children list: its length minus the trailing plain-retain run (found
|
|
157
|
+
* by walking from the right). The first `n` ops from `cs.start` are the content.
|
|
158
|
+
*
|
|
159
|
+
* @param {list.List<ChildrenOpAny>} cs
|
|
160
|
+
* @return {number}
|
|
161
|
+
*/
|
|
162
|
+
const _contentLen = cs => {
|
|
163
|
+
let n = cs.len
|
|
164
|
+
for (let end = cs.end; end !== null && _isPlainRetain(end); end = end.prev) n--
|
|
165
|
+
return n
|
|
166
|
+
}
|
|
167
|
+
|
|
145
168
|
/**
|
|
146
169
|
* Shallow per-key tri-state merge of `update` into `base` (the usual `format`-dimension semantics, also reused
|
|
147
170
|
* as the inner step of {@link mergeAttr}). Per key: `undefined` skips, `null` removes, anything else sets.
|
|
@@ -308,6 +331,10 @@ const combineData = (usedData, arg, deep) => {
|
|
|
308
331
|
return object.isEmpty(r) ? null : /** @type {{[k:string]:any}} */ (r)
|
|
309
332
|
}
|
|
310
333
|
/**
|
|
334
|
+
* Freeze a maybe-delta for a frozen clone (a scalar passes through untouched). `done()` is
|
|
335
|
+
* fingerprint-neutral (it only trims trailing plain retains, which are not fingerprinted), so the
|
|
336
|
+
* clone may carry the op's cached fingerprint over.
|
|
337
|
+
*
|
|
311
338
|
* @template {any} MaybeDelta
|
|
312
339
|
* @param {MaybeDelta} maybeDelta
|
|
313
340
|
* @return {MaybeDelta}
|
|
@@ -329,6 +356,10 @@ const _markMaybeDeltaAsDone = maybeDelta => $deltaAny.check(maybeDelta) ? /** @t
|
|
|
329
356
|
* fingerprint read (and any `diff` / equality check that relies on it) is
|
|
330
357
|
* wrong. Fields covered: insert, delete, retain, format, attribution,
|
|
331
358
|
* value, key.
|
|
359
|
+
* - **A `clone` copies `_fingerprint` when the copy is content-identical:** a full range and the
|
|
360
|
+
* same key. Freezing a nested value with `done()` is fingerprint-neutral (trailing plain retains
|
|
361
|
+
* are not fingerprinted), so nothing else can differ. The root delta returned by
|
|
362
|
+
* `clone`/`slice`/`cloneDeep` never inherits a fingerprint.
|
|
332
363
|
*
|
|
333
364
|
* @internal not part of the consumer API — a content op; build deltas via {@link create}/{@link DeltaBuilder}.
|
|
334
365
|
*/
|
|
@@ -419,7 +450,9 @@ export class TextOp extends list.ListNode {
|
|
|
419
450
|
* @return {TextOp}
|
|
420
451
|
*/
|
|
421
452
|
clone (start = 0, end = this.length, _markAsDone = true) {
|
|
422
|
-
|
|
453
|
+
const cpy = new TextOp(this.insert.slice(start, end), _cloneAttrs(this.format), _cloneAttrs(this.attribution))
|
|
454
|
+
if (start === 0 && end === this.length) cpy._fingerprint = this._fingerprint
|
|
455
|
+
return cpy
|
|
423
456
|
}
|
|
424
457
|
}
|
|
425
458
|
|
|
@@ -541,7 +574,9 @@ export class InsertOp extends list.ListNode {
|
|
|
541
574
|
*/
|
|
542
575
|
clone (start = 0, end = this.length, markAsDone = true) {
|
|
543
576
|
const insert = this.insert.slice(start, end)
|
|
544
|
-
|
|
577
|
+
const cpy = new InsertOp(markAsDone ? insert.map(_markMaybeDeltaAsDone) : insert, _cloneAttrs(this.format), _cloneAttrs(this.attribution))
|
|
578
|
+
if (start === 0 && end === this.length) cpy._fingerprint = this._fingerprint
|
|
579
|
+
return cpy
|
|
545
580
|
}
|
|
546
581
|
}
|
|
547
582
|
|
|
@@ -614,7 +649,9 @@ export class DeleteOp extends list.ListNode {
|
|
|
614
649
|
* @return {DeleteOp}
|
|
615
650
|
*/
|
|
616
651
|
clone (start = 0, end = this.delete, _markAsDone = true) {
|
|
617
|
-
|
|
652
|
+
const cpy = new DeleteOp(end - start)
|
|
653
|
+
if (end - start === this.delete) cpy._fingerprint = this._fingerprint
|
|
654
|
+
return cpy
|
|
618
655
|
}
|
|
619
656
|
}
|
|
620
657
|
|
|
@@ -712,7 +749,9 @@ export class RetainOp extends list.ListNode {
|
|
|
712
749
|
* @return {RetainOp}
|
|
713
750
|
*/
|
|
714
751
|
clone (start = 0, end = this.retain, _markAsDone = true) {
|
|
715
|
-
|
|
752
|
+
const cpy = new RetainOp(end - start, _cloneAttrs(this.format), _cloneAttrs(this.attribution))
|
|
753
|
+
if (end - start === this.retain) cpy._fingerprint = this._fingerprint
|
|
754
|
+
return cpy
|
|
716
755
|
}
|
|
717
756
|
}
|
|
718
757
|
|
|
@@ -829,7 +868,9 @@ export class ModifyOp extends list.ListNode {
|
|
|
829
868
|
// modify is never split (apply's `move ? op : op.clone(0, 1, keep)` evaluates the clone only when
|
|
830
869
|
// `keep` is true). Kept for the uniform children-op `clone` signature.
|
|
831
870
|
/* c8 ignore next */
|
|
832
|
-
|
|
871
|
+
const cpy = new ModifyOp(/** @type {DTypes} */ (markAsDone ? this.value.done() : this.value), _cloneAttrs(this.format), _cloneAttrs(this.attribution))
|
|
872
|
+
cpy._fingerprint = this._fingerprint
|
|
873
|
+
return cpy
|
|
833
874
|
}
|
|
834
875
|
}
|
|
835
876
|
|
|
@@ -916,10 +957,15 @@ export class SetAttrOp {
|
|
|
916
957
|
* Full (frozen) clone. A `move` apply reuses the source op instead of cloning, so there is no
|
|
917
958
|
* shared-mutable variant — see {@link DeltaBuilder#apply}.
|
|
918
959
|
*
|
|
919
|
-
* @
|
|
960
|
+
* @template {string|number} [K2=K]
|
|
961
|
+
* @param {K2} [key] retarget the clone to another attribute key (a transformer projecting or
|
|
962
|
+
* renaming an attribute); defaults to this op's key.
|
|
963
|
+
* @return {SetAttrOp<V,K2>}
|
|
920
964
|
*/
|
|
921
|
-
clone () {
|
|
922
|
-
|
|
965
|
+
clone (key = /** @type {any} */ (this.key)) {
|
|
966
|
+
const cpy = new SetAttrOp(key, _markMaybeDeltaAsDone(this.value), _cloneAttrs(this.attribution))
|
|
967
|
+
if (/** @type {string|number} */ (key) === this.key) cpy._fingerprint = this._fingerprint // the key is part of the fingerprint
|
|
968
|
+
return cpy
|
|
923
969
|
}
|
|
924
970
|
}
|
|
925
971
|
|
|
@@ -983,10 +1029,14 @@ export class DeleteAttrOp {
|
|
|
983
1029
|
/**
|
|
984
1030
|
* Full (frozen) clone; a `move` apply reuses the source op instead — see {@link DeltaBuilder#apply}.
|
|
985
1031
|
*
|
|
986
|
-
* @
|
|
1032
|
+
* @template {string|number} [K2=K]
|
|
1033
|
+
* @param {K2} [key] retarget the clone to another attribute key; defaults to this op's key.
|
|
1034
|
+
* @return {DeleteAttrOp<V,K2>}
|
|
987
1035
|
*/
|
|
988
|
-
clone () {
|
|
989
|
-
|
|
1036
|
+
clone (key = /** @type {any} */ (this.key)) {
|
|
1037
|
+
const cpy = new DeleteAttrOp(key, _cloneAttrs(this.attribution))
|
|
1038
|
+
if (/** @type {string|number} */ (key) === this.key) cpy._fingerprint = this._fingerprint // the key is part of the fingerprint
|
|
1039
|
+
return cpy
|
|
990
1040
|
}
|
|
991
1041
|
}
|
|
992
1042
|
|
|
@@ -1069,10 +1119,14 @@ export class ModifyAttrOp {
|
|
|
1069
1119
|
/**
|
|
1070
1120
|
* Full (frozen) clone; a `move` apply reuses the source op instead — see {@link DeltaBuilder#apply}.
|
|
1071
1121
|
*
|
|
1072
|
-
* @
|
|
1122
|
+
* @template {string|number} [K2=K]
|
|
1123
|
+
* @param {K2} [key] retarget the clone to another attribute key; defaults to this op's key.
|
|
1124
|
+
* @return {ModifyAttrOp<Modifier,K2>}
|
|
1073
1125
|
*/
|
|
1074
|
-
clone () {
|
|
1075
|
-
|
|
1126
|
+
clone (key = /** @type {any} */ (this.key)) {
|
|
1127
|
+
const cpy = new ModifyAttrOp(key, /** @type {Modifier} */ (this.value.done()), _cloneAttrs(this.attribution))
|
|
1128
|
+
if (/** @type {string|number} */ (key) === this.key) cpy._fingerprint = this._fingerprint // the key is part of the fingerprint
|
|
1129
|
+
return cpy
|
|
1076
1130
|
}
|
|
1077
1131
|
}
|
|
1078
1132
|
|
|
@@ -1306,10 +1360,21 @@ export const createMark = (key, id, assoc, attrs, transient) => new Mark(key, id
|
|
|
1306
1360
|
*/
|
|
1307
1361
|
|
|
1308
1362
|
/**
|
|
1309
|
-
* Transform Delta(Builder) to a normal delta
|
|
1363
|
+
* Transform Delta(Builder) to a normal delta: a `DeltaBuilder<Conf>` becomes `Delta<Conf>`, anything
|
|
1364
|
+
* else passes through.
|
|
1365
|
+
*
|
|
1366
|
+
* Infers the conf from `DeltaBuilder` on purpose, not from `Delta<infer Conf>`. lib0's own source
|
|
1367
|
+
* check never had a problem with the latter, but a consumer type-checks the emitted declaration
|
|
1368
|
+
* files, and there matching a builder against `Delta<infer Conf>` is structural: TypeScript walks
|
|
1369
|
+
* `Delta`'s members and recurses through {@link DeltaConfGetChildren}'s recursive branch until its
|
|
1370
|
+
* instantiation-depth limit, so every nested condensed builder (for example
|
|
1371
|
+
* `create().insert([create('c', {}, [create('p', {}, 'x')])])`) failed with TS2589 in
|
|
1372
|
+
* y-prosemirror and yjs. The structural walk also leaked the grandchild conf into the child
|
|
1373
|
+
* union; inferring from the builder keeps the nesting exact. dist-check/consumer.js pins both
|
|
1374
|
+
* against dist.
|
|
1310
1375
|
*
|
|
1311
1376
|
* @template V
|
|
1312
|
-
* @typedef {V extends never ? never : (import('../ts.js').TypeIsAny<V,any,V extends
|
|
1377
|
+
* @typedef {V extends never ? never : (import('../ts.js').TypeIsAny<V,any,V extends DeltaBuilder<infer Conf, any> ? Delta<Conf> : V>)} _SanifyDelta
|
|
1313
1378
|
*/
|
|
1314
1379
|
|
|
1315
1380
|
/**
|
|
@@ -1443,8 +1508,13 @@ export class Delta extends DeltaData {
|
|
|
1443
1508
|
for (const key of keys) {
|
|
1444
1509
|
encoding.writeVarString(encoder, /** @type {any} */ (this.attrs[/** @type {keyof typeof this.attrs} */ (key)]).fingerprint)
|
|
1445
1510
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1511
|
+
// trailing plain retains are not content: `done()` trims them, and the fingerprint must not
|
|
1512
|
+
// change when it does — so only the children up to the last content op are fingerprinted
|
|
1513
|
+
const cs = this.children
|
|
1514
|
+
let n = _contentLen(cs)
|
|
1515
|
+
encoding.writeVarUint(encoder, n)
|
|
1516
|
+
// non-null for the first `n` ops by construction (see _contentLen)
|
|
1517
|
+
for (let child = /** @type {ChildrenOpAny} */ (cs.start); n > 0; n--, child = /** @type {ChildrenOpAny} */ (child.next)) {
|
|
1448
1518
|
encoding.writeVarString(encoder, child.fingerprint)
|
|
1449
1519
|
}
|
|
1450
1520
|
}))))
|
|
@@ -1455,7 +1525,8 @@ export class Delta extends DeltaData {
|
|
|
1455
1525
|
}
|
|
1456
1526
|
|
|
1457
1527
|
isEmpty () {
|
|
1458
|
-
|
|
1528
|
+
// a children list holding only plain retains is empty (positional only — see _isPlainRetain)
|
|
1529
|
+
return object.isEmpty(this.attrs) && _contentLen(this.children) === 0 && (this.marks === null || this.marks.size === 0) && (this.deleteMarks === null || this.deleteMarks.size === 0)
|
|
1459
1530
|
}
|
|
1460
1531
|
|
|
1461
1532
|
/**
|
|
@@ -1500,10 +1571,20 @@ export class Delta extends DeltaData {
|
|
|
1500
1571
|
* @return {boolean}
|
|
1501
1572
|
*/
|
|
1502
1573
|
[equalityTrait.EqualityTraitSymbol] (other) {
|
|
1503
|
-
// @todo it is only necessary to compare finrerprints OR do a deep equality check (remove
|
|
1504
|
-
// childCnt as well)
|
|
1505
1574
|
// marks are local/ephemeral cursor state and intentionally NOT part of document identity
|
|
1506
|
-
|
|
1575
|
+
if (this.name !== other.name || !fun.equalityDeep(this.attrs, other.attrs)) return false
|
|
1576
|
+
// only the content ops count (a trailing plain-retain run is not content — see _isPlainRetain);
|
|
1577
|
+
// comparing the counts first also makes two retain-only lists equal. `childCnt` (Σ op.length)
|
|
1578
|
+
// needs no separate check: every content op's equality covers its length-bearing field.
|
|
1579
|
+
const n = _contentLen(this.children)
|
|
1580
|
+
if (n !== _contentLen(other.children)) return false
|
|
1581
|
+
// non-null for the first `n` ops by construction (see _contentLen)
|
|
1582
|
+
let a = /** @type {ChildrenOpAny} */ (this.children.start)
|
|
1583
|
+
let b = /** @type {ChildrenOpAny} */ (other.children.start)
|
|
1584
|
+
for (let i = 0; i < n; i++, a = /** @type {ChildrenOpAny} */ (a.next), b = /** @type {ChildrenOpAny} */ (b.next)) {
|
|
1585
|
+
if (!fun.equalityDeep(a, b)) return false
|
|
1586
|
+
}
|
|
1587
|
+
return true
|
|
1507
1588
|
}
|
|
1508
1589
|
|
|
1509
1590
|
// toString () {
|
|
@@ -1531,11 +1612,12 @@ export class Delta extends DeltaData {
|
|
|
1531
1612
|
done (markAsDone = true) {
|
|
1532
1613
|
if (!this.isDone) {
|
|
1533
1614
|
this.isDone = markAsDone
|
|
1615
|
+
// trim the trailing plain-retain run — fingerprint-neutral, as fingerprint/equality never counted
|
|
1616
|
+
// it (see _contentLen); any other cleanup added here must null `_fingerprint`
|
|
1534
1617
|
const cs = this.children
|
|
1535
|
-
for (let end = cs.end; end !== null &&
|
|
1618
|
+
for (let end = cs.end; end !== null && _isPlainRetain(end); end = cs.end) {
|
|
1536
1619
|
this.childCnt -= end.length
|
|
1537
1620
|
list.popEnd(cs)
|
|
1538
|
-
this._fingerprint = null
|
|
1539
1621
|
}
|
|
1540
1622
|
}
|
|
1541
1623
|
return this
|
|
@@ -1631,22 +1713,48 @@ export const clone = d => /** @type {any} */ (slice(d, 0, d.childCnt))
|
|
|
1631
1713
|
* @param {any} v
|
|
1632
1714
|
* @return {any}
|
|
1633
1715
|
*/
|
|
1634
|
-
const _cloneMaybeDeltaDeep = v => $deltaAny.check(v) ?
|
|
1716
|
+
const _cloneMaybeDeltaDeep = v => $deltaAny.check(v) ? _cloneDeepNested(v) : v
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* Carry `src`'s cached fingerprint over to `cpy`, a content-identical copy of it. Only valid when
|
|
1720
|
+
* nothing about the copy differs from the source in fingerprinted fields (a full-range clone, the same
|
|
1721
|
+
* key, nested values cloned rather than `done()`-trimmed).
|
|
1722
|
+
*
|
|
1723
|
+
* @template {{ _fingerprint: string|null }} T
|
|
1724
|
+
* @param {{ _fingerprint: string|null }} src
|
|
1725
|
+
* @param {T} cpy
|
|
1726
|
+
* @return {T}
|
|
1727
|
+
*/
|
|
1728
|
+
const _copyFp = (src, cpy) => {
|
|
1729
|
+
cpy._fingerprint = src._fingerprint
|
|
1730
|
+
return cpy
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* {@link cloneDeep} for a *nested* delta: content-identical, so the cached fingerprint rides along
|
|
1735
|
+
* (only the root returned by the exported `cloneDeep` starts without one).
|
|
1736
|
+
*
|
|
1737
|
+
* @template {DeltaConf} Conf
|
|
1738
|
+
* @param {Delta<Conf>} d
|
|
1739
|
+
* @return {DeltaBuilder<Conf>}
|
|
1740
|
+
*/
|
|
1741
|
+
const _cloneDeepNested = d => _copyFp(d, cloneDeep(d))
|
|
1635
1742
|
|
|
1636
1743
|
/**
|
|
1637
1744
|
* Deep-clone one content (child) op for {@link cloneDeep}: a fresh op whose nested deltas (an insert's
|
|
1638
1745
|
* delta content, a modify's value) are themselves deep-cloned. `format`/`attribution` objects are
|
|
1639
1746
|
* retained (shared) — they are always copied before being mutated (see {@link cloneDeep}). Ops without a
|
|
1640
|
-
* nested delta (`text`/`retain`/`delete`) use `op.clone()`.
|
|
1747
|
+
* nested delta (`text`/`retain`/`delete`) use `op.clone()`. Every copy is content-identical, so the
|
|
1748
|
+
* cached fingerprint is carried over.
|
|
1641
1749
|
*
|
|
1642
1750
|
* @param {ChildrenOpAny} op
|
|
1643
1751
|
* @return {ChildrenOpAny}
|
|
1644
1752
|
*/
|
|
1645
1753
|
const _cloneChildOpDeep = op =>
|
|
1646
1754
|
$insertOp.check(op)
|
|
1647
|
-
? new InsertOp(op.insert.map(_cloneMaybeDeltaDeep), op.format, op.attribution)
|
|
1755
|
+
? _copyFp(op, new InsertOp(op.insert.map(_cloneMaybeDeltaDeep), op.format, op.attribution))
|
|
1648
1756
|
: ($modifyOp.check(op)
|
|
1649
|
-
? new ModifyOp(
|
|
1757
|
+
? _copyFp(op, new ModifyOp(_cloneDeepNested(op.value), op.format, op.attribution))
|
|
1650
1758
|
: op.clone())
|
|
1651
1759
|
|
|
1652
1760
|
/**
|
|
@@ -1659,17 +1767,18 @@ const _cloneChildOpDeep = op =>
|
|
|
1659
1767
|
*/
|
|
1660
1768
|
const _cloneAttrOpDeep = op =>
|
|
1661
1769
|
$setAttrOp.check(op)
|
|
1662
|
-
? new SetAttrOp(op.key, _cloneMaybeDeltaDeep(op.value), op.attribution)
|
|
1770
|
+
? _copyFp(op, new SetAttrOp(op.key, _cloneMaybeDeltaDeep(op.value), op.attribution))
|
|
1663
1771
|
: ($modifyAttrOp.check(op)
|
|
1664
|
-
? new ModifyAttrOp(op.key,
|
|
1772
|
+
? _copyFp(op, new ModifyAttrOp(op.key, _cloneDeepNested(op.value), op.attribution))
|
|
1665
1773
|
// the only remaining attr op is a deleteAttr
|
|
1666
|
-
: new DeleteAttrOp(op.key, op.attribution))
|
|
1774
|
+
: _copyFp(op, new DeleteAttrOp(op.key, op.attribution)))
|
|
1667
1775
|
|
|
1668
1776
|
/**
|
|
1669
1777
|
* A **deep** clone of `d`: like {@link clone}, but every nested delta — an insert's delta content, a
|
|
1670
1778
|
* `modify`/`modifyAttr` value, a delta-valued attribute — is itself recursively cloned into a fresh,
|
|
1671
1779
|
* **mutable** node, instead of being frozen (`done`) and shared as {@link clone} does. The result and
|
|
1672
|
-
* its whole subtree are therefore independently editable.
|
|
1780
|
+
* its whole subtree are therefore independently editable. Cached fingerprints of the ops and nested
|
|
1781
|
+
* deltas ride along (they are content-identical); only the returned root starts without one.
|
|
1673
1782
|
*
|
|
1674
1783
|
* ## What is cloned, and when to reach for this
|
|
1675
1784
|
*
|
|
@@ -1773,7 +1882,7 @@ const modValue = op => {
|
|
|
1773
1882
|
* These are the ONLY sanctioned way to structurally edit `d.children` in place. Each keeps the cached
|
|
1774
1883
|
* invariant `d.childCnt === Σ op.length` correct and resets the op's `_fingerprint`. Never mutate
|
|
1775
1884
|
* `op.retain` / `op.delete` / `op.insert`, call `list.*`, or touch `d.childCnt` directly from outside
|
|
1776
|
-
* delta.js — go through these so the count (which `
|
|
1885
|
+
* delta.js — go through these so the count (which `slice`/`apply` position by) never drifts.
|
|
1777
1886
|
*
|
|
1778
1887
|
* Cursor pattern (used by {@link DeltaBuilder#apply}, {@link DeltaBuilder#rebase}, and
|
|
1779
1888
|
* `transformer/conform.js`): walk the target with `(op, offset)`; {@link _splitChildAt} before inserting
|
|
@@ -17,11 +17,9 @@ import { Transformer, Template, createTransformResult, attrsShapeOf } from './co
|
|
|
17
17
|
const attrTransformHelper = (outDelta, from, to, inDelta) => {
|
|
18
18
|
const attrOp = inDelta.attrs[from]
|
|
19
19
|
if (attrOp != null) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// a mapped type over fixed conf keys, so
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
c.key = to
|
|
20
|
+
// a clone retargeted to `to` (the clone skips the cached fingerprint, which covers the key)
|
|
21
|
+
const c = attrOp.clone(to)
|
|
22
|
+
// `attrs` is a mapped type over fixed conf keys, so the dynamic-key write needs the cast
|
|
25
23
|
const oattrs = /** @type {any} */ (outDelta.attrs)
|
|
26
24
|
oattrs[to] = c
|
|
27
25
|
}
|
|
@@ -21,12 +21,11 @@ const renameDeltaAttrs = (d, renames, revRenames) => {
|
|
|
21
21
|
const r = renames[key]
|
|
22
22
|
const rv = revRenames[key]
|
|
23
23
|
if (r != null) {
|
|
24
|
+
// a clone retargeted to `r` (the clone skips the cached fingerprint, which covers the key)
|
|
24
25
|
// @ts-ignore
|
|
25
|
-
forwardTransform.attrs[r] = attr
|
|
26
|
+
forwardTransform.attrs[r] = attr.clone(r)
|
|
26
27
|
// delete original
|
|
27
28
|
delete forwardTransform.attrs[key]
|
|
28
|
-
// @ts-ignore
|
|
29
|
-
attr.key = r
|
|
30
29
|
} else if (rv != null) {
|
|
31
30
|
// used in a rename, delete original
|
|
32
31
|
delete forwardTransform.attrs[key]
|