@fluidframework/tree 2.80.0 → 2.81.0-374083
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/api-report/tree.alpha.api.md +43 -7
- package/dist/alpha.d.ts +5 -0
- package/dist/core/change-family/changeFamily.d.ts +4 -1
- package/dist/core/change-family/changeFamily.d.ts.map +1 -1
- package/dist/core/change-family/changeFamily.js.map +1 -1
- package/dist/core/change-family/index.d.ts +1 -1
- package/dist/core/change-family/index.d.ts.map +1 -1
- package/dist/core/change-family/index.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/rebase/changeRebaser.d.ts +6 -1
- package/dist/core/rebase/changeRebaser.d.ts.map +1 -1
- package/dist/core/rebase/changeRebaser.js.map +1 -1
- package/dist/core/rebase/index.d.ts +1 -1
- package/dist/core/rebase/index.d.ts.map +1 -1
- package/dist/core/rebase/index.js +2 -1
- package/dist/core/rebase/index.js.map +1 -1
- package/dist/core/rebase/types.d.ts +46 -8
- package/dist/core/rebase/types.d.ts.map +1 -1
- package/dist/core/rebase/types.js +5 -1
- package/dist/core/rebase/types.js.map +1 -1
- package/dist/core/rebase/utils.d.ts.map +1 -1
- package/dist/core/rebase/utils.js +30 -8
- package/dist/core/rebase/utils.js.map +1 -1
- package/dist/core/tree/anchorSet.js +1 -0
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/core/tree/detachedFieldIndex.d.ts +6 -0
- package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndex.js +9 -0
- package/dist/core/tree/detachedFieldIndex.js.map +1 -1
- package/dist/core/tree/index.d.ts +1 -1
- package/dist/core/tree/index.d.ts.map +1 -1
- package/dist/core/tree/index.js +4 -3
- package/dist/core/tree/index.js.map +1 -1
- package/dist/core/tree/pathTree.d.ts +11 -3
- package/dist/core/tree/pathTree.d.ts.map +1 -1
- package/dist/core/tree/pathTree.js +14 -2
- package/dist/core/tree/pathTree.js.map +1 -1
- package/dist/core/tree/visitDelta.d.ts.map +1 -1
- package/dist/core/tree/visitDelta.js +3 -2
- package/dist/core/tree/visitDelta.js.map +1 -1
- package/dist/core/tree/visitorUtils.d.ts.map +1 -1
- package/dist/core/tree/visitorUtils.js +58 -18
- package/dist/core/tree/visitorUtils.js.map +1 -1
- package/dist/feature-libraries/changeAtomIdBTree.d.ts +10 -4
- package/dist/feature-libraries/changeAtomIdBTree.d.ts.map +1 -1
- package/dist/feature-libraries/changeAtomIdBTree.js +16 -2
- package/dist/feature-libraries/changeAtomIdBTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/basicChunk.js +7 -0
- package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js +4 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +3 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js +1 -0
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts +93 -44
- package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
- package/dist/feature-libraries/default-schema/defaultEditBuilder.js +238 -69
- package/dist/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
- package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
- package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
- package/dist/feature-libraries/default-schema/defaultFieldKinds.js +11 -2
- package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
- package/dist/feature-libraries/default-schema/index.d.ts +2 -1
- package/dist/feature-libraries/default-schema/index.d.ts.map +1 -1
- package/dist/feature-libraries/default-schema/index.js +5 -2
- package/dist/feature-libraries/default-schema/index.js.map +1 -1
- package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
- package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
- package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js +132 -0
- package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
- package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts +7 -6
- package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
- package/dist/feature-libraries/default-schema/mappedEditBuilder.js +15 -0
- package/dist/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
- package/dist/feature-libraries/deltaUtils.d.ts +1 -0
- package/dist/feature-libraries/deltaUtils.d.ts.map +1 -1
- package/dist/feature-libraries/deltaUtils.js +6 -1
- package/dist/feature-libraries/deltaUtils.js.map +1 -1
- package/dist/feature-libraries/flex-tree/context.d.ts +9 -0
- package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/context.js +6 -0
- package/dist/feature-libraries/flex-tree/context.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts +8 -7
- package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +40 -9
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +3 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +3 -3
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +8 -3
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/mapTreeCursor.d.ts.map +1 -1
- package/dist/feature-libraries/mapTreeCursor.js +1 -0
- package/dist/feature-libraries/mapTreeCursor.js.map +1 -1
- package/dist/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/mitigatedChangeFamily.js +2 -2
- package/dist/feature-libraries/mitigatedChangeFamily.js.map +1 -1
- package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
- package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/crossFieldQueries.js +4 -7
- package/dist/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +20 -51
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
- package/dist/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/genericFieldKind.js +3 -9
- package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts +4 -4
- package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/index.js +2 -2
- package/dist/feature-libraries/modular-schema/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +11 -28
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +255 -161
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
- package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
- package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js +393 -0
- package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +8 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +48 -20
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +1350 -476
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
- package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
- package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js +32 -0
- package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +47 -11
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.js +3 -3
- package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +3 -1
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/feature-libraries/optional-field/index.d.ts +2 -2
- package/dist/feature-libraries/optional-field/index.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/index.js +1 -2
- package/dist/feature-libraries/optional-field/index.js.map +1 -1
- package/dist/feature-libraries/optional-field/optionalField.d.ts +5 -26
- package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/optionalField.js +221 -443
- package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
- package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
- package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +31 -0
- package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
- package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
- package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js +57 -28
- package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
- package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
- package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js +57 -0
- package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
- package/dist/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
- package/dist/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
- package/dist/feature-libraries/sequence-field/compose.d.ts +6 -7
- package/dist/feature-libraries/sequence-field/compose.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/compose.js +83 -259
- package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
- package/dist/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
- package/dist/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/helperTypes.js.map +1 -1
- package/dist/feature-libraries/sequence-field/index.d.ts +2 -3
- package/dist/feature-libraries/sequence-field/index.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/index.js +1 -3
- package/dist/feature-libraries/sequence-field/index.js.map +1 -1
- package/dist/feature-libraries/sequence-field/invert.d.ts +3 -3
- package/dist/feature-libraries/sequence-field/invert.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/invert.js +65 -167
- package/dist/feature-libraries/sequence-field/invert.js.map +1 -1
- package/dist/feature-libraries/sequence-field/markQueue.d.ts +2 -2
- package/dist/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/markQueue.js.map +1 -1
- package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
- package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/moveEffectTable.js +7 -90
- package/dist/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
- package/dist/feature-libraries/sequence-field/rebase.d.ts +3 -3
- package/dist/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/rebase.js +107 -114
- package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
- package/dist/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/replaceRevisions.js +18 -31
- package/dist/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
- package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js +365 -187
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js +20 -62
- package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
- package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
- package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
- package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js +14 -109
- package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
- package/dist/feature-libraries/sequence-field/types.d.ts +30 -59
- package/dist/feature-libraries/sequence-field/types.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/types.js.map +1 -1
- package/dist/feature-libraries/sequence-field/utils.d.ts +15 -24
- package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/utils.js +116 -305
- package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/index.d.ts +1 -1
- package/dist/shared-tree/index.d.ts.map +1 -1
- package/dist/shared-tree/index.js.map +1 -1
- package/dist/shared-tree/schematizeTree.d.ts +4 -4
- package/dist/shared-tree/schematizeTree.d.ts.map +1 -1
- package/dist/shared-tree/schematizeTree.js +2 -1
- package/dist/shared-tree/schematizeTree.js.map +1 -1
- package/dist/shared-tree/schematizingTreeView.d.ts +1 -5
- package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/dist/shared-tree/schematizingTreeView.js +38 -35
- package/dist/shared-tree/schematizingTreeView.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +9 -3
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +11 -0
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js +1 -0
- package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeFamily.d.ts +5 -5
- package/dist/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeChangeFamily.js +10 -4
- package/dist/shared-tree/sharedTreeChangeFamily.js.map +1 -1
- package/dist/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
- package/dist/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeEditBuilder.js +14 -7
- package/dist/shared-tree/sharedTreeEditBuilder.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +9 -10
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +63 -8
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +3 -2
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js +9 -4
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/editManager.d.ts +2 -2
- package/dist/shared-tree-core/editManager.d.ts.map +1 -1
- package/dist/shared-tree-core/editManager.js +20 -14
- package/dist/shared-tree-core/editManager.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.d.ts +4 -0
- package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.js +10 -2
- package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/dist/shared-tree-core/editManagerFormatCommons.d.ts +1 -0
- package/dist/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerFormatCommons.js +6 -0
- package/dist/shared-tree-core/editManagerFormatCommons.js.map +1 -1
- package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
- package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerFormatV1toV4.js +1 -0
- package/dist/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
- package/dist/shared-tree-core/index.d.ts +2 -2
- package/dist/shared-tree-core/index.d.ts.map +1 -1
- package/dist/shared-tree-core/index.js +3 -1
- package/dist/shared-tree-core/index.js.map +1 -1
- package/dist/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
- package/dist/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
- package/dist/shared-tree-core/messageCodecs.d.ts +4 -0
- package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecs.js +10 -2
- package/dist/shared-tree-core/messageCodecs.js.map +1 -1
- package/dist/shared-tree-core/messageFormat.d.ts +1 -0
- package/dist/shared-tree-core/messageFormat.d.ts.map +1 -1
- package/dist/shared-tree-core/messageFormat.js +6 -0
- package/dist/shared-tree-core/messageFormat.js.map +1 -1
- package/dist/shared-tree-core/messageFormatV1ToV4.d.ts +2 -2
- package/dist/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
- package/dist/shared-tree-core/messageFormatV1ToV4.js +1 -0
- package/dist/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts +1 -0
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/api/index.d.ts +1 -1
- package/dist/simple-tree/api/index.d.ts.map +1 -1
- package/dist/simple-tree/api/index.js +2 -1
- package/dist/simple-tree/api/index.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryAlpha.js +1 -1
- package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryBeta.js +1 -1
- package/dist/simple-tree/api/schemaFactoryBeta.js.map +1 -1
- package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +4 -4
- package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js +297 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/dist/simple-tree/api/tree.d.ts +3 -1
- package/dist/simple-tree/api/tree.d.ts.map +1 -1
- package/dist/simple-tree/api/tree.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +6 -2
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.js +59 -8
- package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/dist/simple-tree/fieldSchema.d.ts +4 -4
- package/dist/simple-tree/fieldSchema.d.ts.map +1 -1
- package/dist/simple-tree/fieldSchema.js.map +1 -1
- package/dist/simple-tree/index.d.ts +3 -3
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +4 -3
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +7 -5
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/common.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/common.js +1 -1
- package/dist/simple-tree/node-kinds/common.js.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.js +2 -2
- package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/object/objectNode.js +19 -19
- package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/record/recordNode.js +4 -2
- package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/dist/simple-tree/prepareForInsertion.d.ts +54 -47
- package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
- package/dist/simple-tree/prepareForInsertion.js +184 -126
- package/dist/simple-tree/prepareForInsertion.js.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +31 -13
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/dist/text/index.d.ts +6 -0
- package/dist/text/index.d.ts.map +1 -0
- package/dist/text/index.js +10 -0
- package/dist/text/index.js.map +1 -0
- package/dist/text/textDomain.d.ts +138 -0
- package/dist/text/textDomain.d.ts.map +1 -0
- package/dist/text/textDomain.js +121 -0
- package/dist/text/textDomain.js.map +1 -0
- package/dist/treeFactory.d.ts.map +1 -1
- package/dist/treeFactory.js +12 -2
- package/dist/treeFactory.js.map +1 -1
- package/dist/util/bTreeUtils.d.ts +12 -4
- package/dist/util/bTreeUtils.d.ts.map +1 -1
- package/dist/util/bTreeUtils.js +14 -18
- package/dist/util/bTreeUtils.js.map +1 -1
- package/dist/util/index.d.ts +1 -1
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +2 -1
- package/dist/util/index.js.map +1 -1
- package/dist/util/nestedMap.js +12 -12
- package/dist/util/nestedMap.js.map +1 -1
- package/dist/util/rangeMap.d.ts +24 -12
- package/dist/util/rangeMap.d.ts.map +1 -1
- package/dist/util/rangeMap.js +46 -6
- package/dist/util/rangeMap.js.map +1 -1
- package/docs/user-facing/merge-semantics.md +3 -2
- package/eslint.config.mts +4 -32
- package/lib/alpha.d.ts +5 -0
- package/lib/core/change-family/changeFamily.d.ts +4 -1
- package/lib/core/change-family/changeFamily.d.ts.map +1 -1
- package/lib/core/change-family/changeFamily.js.map +1 -1
- package/lib/core/change-family/index.d.ts +1 -1
- package/lib/core/change-family/index.d.ts.map +1 -1
- package/lib/core/change-family/index.js.map +1 -1
- package/lib/core/index.d.ts +3 -3
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +2 -2
- package/lib/core/index.js.map +1 -1
- package/lib/core/rebase/changeRebaser.d.ts +6 -1
- package/lib/core/rebase/changeRebaser.d.ts.map +1 -1
- package/lib/core/rebase/changeRebaser.js.map +1 -1
- package/lib/core/rebase/index.d.ts +1 -1
- package/lib/core/rebase/index.d.ts.map +1 -1
- package/lib/core/rebase/index.js +1 -1
- package/lib/core/rebase/index.js.map +1 -1
- package/lib/core/rebase/types.d.ts +46 -8
- package/lib/core/rebase/types.d.ts.map +1 -1
- package/lib/core/rebase/types.js +3 -0
- package/lib/core/rebase/types.js.map +1 -1
- package/lib/core/rebase/utils.d.ts.map +1 -1
- package/lib/core/rebase/utils.js +30 -8
- package/lib/core/rebase/utils.js.map +1 -1
- package/lib/core/tree/anchorSet.js +1 -0
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/core/tree/detachedFieldIndex.d.ts +6 -0
- package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndex.js +10 -1
- package/lib/core/tree/detachedFieldIndex.js.map +1 -1
- package/lib/core/tree/index.d.ts +1 -1
- package/lib/core/tree/index.d.ts.map +1 -1
- package/lib/core/tree/index.js +1 -1
- package/lib/core/tree/index.js.map +1 -1
- package/lib/core/tree/pathTree.d.ts +11 -3
- package/lib/core/tree/pathTree.d.ts.map +1 -1
- package/lib/core/tree/pathTree.js +12 -1
- package/lib/core/tree/pathTree.js.map +1 -1
- package/lib/core/tree/visitDelta.d.ts.map +1 -1
- package/lib/core/tree/visitDelta.js +3 -2
- package/lib/core/tree/visitDelta.js.map +1 -1
- package/lib/core/tree/visitorUtils.d.ts.map +1 -1
- package/lib/core/tree/visitorUtils.js +58 -18
- package/lib/core/tree/visitorUtils.js.map +1 -1
- package/lib/feature-libraries/changeAtomIdBTree.d.ts +10 -4
- package/lib/feature-libraries/changeAtomIdBTree.d.ts.map +1 -1
- package/lib/feature-libraries/changeAtomIdBTree.js +14 -1
- package/lib/feature-libraries/changeAtomIdBTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.js +8 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js +4 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +3 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js +1 -0
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts +93 -44
- package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
- package/lib/feature-libraries/default-schema/defaultEditBuilder.js +236 -70
- package/lib/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
- package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
- package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
- package/lib/feature-libraries/default-schema/defaultFieldKinds.js +11 -2
- package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
- package/lib/feature-libraries/default-schema/index.d.ts +2 -1
- package/lib/feature-libraries/default-schema/index.d.ts.map +1 -1
- package/lib/feature-libraries/default-schema/index.js +2 -1
- package/lib/feature-libraries/default-schema/index.js.map +1 -1
- package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
- package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
- package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js +128 -0
- package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
- package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts +7 -6
- package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
- package/lib/feature-libraries/default-schema/mappedEditBuilder.js +15 -0
- package/lib/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
- package/lib/feature-libraries/deltaUtils.d.ts +1 -0
- package/lib/feature-libraries/deltaUtils.d.ts.map +1 -1
- package/lib/feature-libraries/deltaUtils.js +5 -1
- package/lib/feature-libraries/deltaUtils.js.map +1 -1
- package/lib/feature-libraries/flex-tree/context.d.ts +9 -0
- package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/context.js +6 -0
- package/lib/feature-libraries/flex-tree/context.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts +8 -7
- package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +41 -10
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +3 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +3 -3
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +2 -2
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/mapTreeCursor.d.ts.map +1 -1
- package/lib/feature-libraries/mapTreeCursor.js +2 -1
- package/lib/feature-libraries/mapTreeCursor.js.map +1 -1
- package/lib/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/mitigatedChangeFamily.js +2 -2
- package/lib/feature-libraries/mitigatedChangeFamily.js.map +1 -1
- package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
- package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/crossFieldQueries.js +3 -5
- package/lib/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +20 -51
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
- package/lib/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/genericFieldKind.js +3 -9
- package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts +4 -4
- package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/index.js +2 -2
- package/lib/feature-libraries/modular-schema/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +11 -28
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +247 -143
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
- package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
- package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js +389 -0
- package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +8 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +48 -20
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1344 -479
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
- package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
- package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js +29 -0
- package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +47 -11
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.js +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +3 -1
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/feature-libraries/optional-field/index.d.ts +2 -2
- package/lib/feature-libraries/optional-field/index.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/index.js +1 -1
- package/lib/feature-libraries/optional-field/index.js.map +1 -1
- package/lib/feature-libraries/optional-field/optionalField.d.ts +5 -26
- package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/optionalField.js +221 -441
- package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
- package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
- package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +27 -0
- package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
- package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
- package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js +55 -26
- package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
- package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
- package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js +53 -0
- package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
- package/lib/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
- package/lib/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
- package/lib/feature-libraries/sequence-field/compose.d.ts +6 -7
- package/lib/feature-libraries/sequence-field/compose.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/compose.js +85 -261
- package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
- package/lib/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
- package/lib/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/helperTypes.js.map +1 -1
- package/lib/feature-libraries/sequence-field/index.d.ts +2 -3
- package/lib/feature-libraries/sequence-field/index.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/index.js +0 -1
- package/lib/feature-libraries/sequence-field/index.js.map +1 -1
- package/lib/feature-libraries/sequence-field/invert.d.ts +3 -3
- package/lib/feature-libraries/sequence-field/invert.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/invert.js +67 -169
- package/lib/feature-libraries/sequence-field/invert.js.map +1 -1
- package/lib/feature-libraries/sequence-field/markQueue.d.ts +2 -2
- package/lib/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/markQueue.js.map +1 -1
- package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
- package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/moveEffectTable.js +6 -84
- package/lib/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
- package/lib/feature-libraries/sequence-field/rebase.d.ts +3 -3
- package/lib/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/rebase.js +109 -116
- package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
- package/lib/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/replaceRevisions.js +18 -31
- package/lib/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
- package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js +356 -182
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js +21 -63
- package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
- package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
- package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
- package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js +14 -109
- package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
- package/lib/feature-libraries/sequence-field/types.d.ts +30 -59
- package/lib/feature-libraries/sequence-field/types.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/types.js.map +1 -1
- package/lib/feature-libraries/sequence-field/utils.d.ts +15 -24
- package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/utils.js +112 -298
- package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/index.d.ts +1 -1
- package/lib/shared-tree/index.d.ts.map +1 -1
- package/lib/shared-tree/index.js.map +1 -1
- package/lib/shared-tree/schematizeTree.d.ts +4 -4
- package/lib/shared-tree/schematizeTree.d.ts.map +1 -1
- package/lib/shared-tree/schematizeTree.js +3 -2
- package/lib/shared-tree/schematizeTree.js.map +1 -1
- package/lib/shared-tree/schematizingTreeView.d.ts +1 -5
- package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/lib/shared-tree/schematizingTreeView.js +41 -38
- package/lib/shared-tree/schematizingTreeView.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +9 -3
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +11 -0
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js +1 -0
- package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js +2 -2
- package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeFamily.d.ts +5 -5
- package/lib/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeChangeFamily.js +11 -5
- package/lib/shared-tree/sharedTreeChangeFamily.js.map +1 -1
- package/lib/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
- package/lib/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeEditBuilder.js +12 -6
- package/lib/shared-tree/sharedTreeEditBuilder.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +9 -10
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +66 -11
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +3 -2
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js +9 -4
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/editManager.d.ts +2 -2
- package/lib/shared-tree-core/editManager.d.ts.map +1 -1
- package/lib/shared-tree-core/editManager.js +20 -14
- package/lib/shared-tree-core/editManager.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.d.ts +4 -0
- package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.js +8 -1
- package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/lib/shared-tree-core/editManagerFormatCommons.d.ts +1 -0
- package/lib/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerFormatCommons.js +6 -0
- package/lib/shared-tree-core/editManagerFormatCommons.js.map +1 -1
- package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
- package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerFormatV1toV4.js +1 -0
- package/lib/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
- package/lib/shared-tree-core/index.d.ts +2 -2
- package/lib/shared-tree-core/index.d.ts.map +1 -1
- package/lib/shared-tree-core/index.js +2 -2
- package/lib/shared-tree-core/index.js.map +1 -1
- package/lib/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
- package/lib/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts +4 -0
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js +8 -1
- package/lib/shared-tree-core/messageCodecs.js.map +1 -1
- package/lib/shared-tree-core/messageFormat.d.ts +1 -0
- package/lib/shared-tree-core/messageFormat.d.ts.map +1 -1
- package/lib/shared-tree-core/messageFormat.js +6 -0
- package/lib/shared-tree-core/messageFormat.js.map +1 -1
- package/lib/shared-tree-core/messageFormatV1ToV4.d.ts +2 -2
- package/lib/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
- package/lib/shared-tree-core/messageFormatV1ToV4.js +1 -0
- package/lib/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts +1 -0
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/api/index.d.ts +1 -1
- package/lib/simple-tree/api/index.d.ts.map +1 -1
- package/lib/simple-tree/api/index.js +1 -1
- package/lib/simple-tree/api/index.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryAlpha.js +1 -1
- package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryBeta.js +1 -1
- package/lib/simple-tree/api/schemaFactoryBeta.js.map +1 -1
- package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +4 -4
- package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js +270 -0
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/lib/simple-tree/api/tree.d.ts +3 -1
- package/lib/simple-tree/api/tree.d.ts.map +1 -1
- package/lib/simple-tree/api/tree.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +6 -2
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.js +58 -8
- package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/lib/simple-tree/fieldSchema.d.ts +4 -4
- package/lib/simple-tree/fieldSchema.d.ts.map +1 -1
- package/lib/simple-tree/fieldSchema.js.map +1 -1
- package/lib/simple-tree/index.d.ts +3 -3
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js +2 -2
- package/lib/simple-tree/index.js.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.js +8 -6
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/common.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/common.js +2 -2
- package/lib/simple-tree/node-kinds/common.js.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.js +2 -2
- package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/object/objectNode.js +20 -20
- package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/record/recordNode.js +4 -2
- package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/lib/simple-tree/prepareForInsertion.d.ts +54 -47
- package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
- package/lib/simple-tree/prepareForInsertion.js +184 -125
- package/lib/simple-tree/prepareForInsertion.js.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +28 -11
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/lib/text/index.d.ts +6 -0
- package/lib/text/index.d.ts.map +1 -0
- package/lib/text/index.js +6 -0
- package/lib/text/index.js.map +1 -0
- package/lib/text/textDomain.d.ts +138 -0
- package/lib/text/textDomain.d.ts.map +1 -0
- package/lib/text/textDomain.js +118 -0
- package/lib/text/textDomain.js.map +1 -0
- package/lib/treeFactory.d.ts.map +1 -1
- package/lib/treeFactory.js +13 -3
- package/lib/treeFactory.js.map +1 -1
- package/lib/util/bTreeUtils.d.ts +12 -4
- package/lib/util/bTreeUtils.d.ts.map +1 -1
- package/lib/util/bTreeUtils.js +15 -19
- package/lib/util/bTreeUtils.js.map +1 -1
- package/lib/util/index.d.ts +1 -1
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +1 -1
- package/lib/util/index.js.map +1 -1
- package/lib/util/nestedMap.js +12 -12
- package/lib/util/nestedMap.js.map +1 -1
- package/lib/util/rangeMap.d.ts +24 -12
- package/lib/util/rangeMap.d.ts.map +1 -1
- package/lib/util/rangeMap.js +44 -5
- package/lib/util/rangeMap.js.map +1 -1
- package/package.json +25 -25
- package/src/core/change-family/changeFamily.ts +5 -0
- package/src/core/change-family/index.ts +1 -0
- package/src/core/index.ts +6 -1
- package/src/core/rebase/changeRebaser.ts +6 -1
- package/src/core/rebase/index.ts +3 -0
- package/src/core/rebase/types.ts +65 -21
- package/src/core/rebase/utils.ts +39 -11
- package/src/core/tree/anchorSet.ts +1 -0
- package/src/core/tree/detachedFieldIndex.ts +17 -1
- package/src/core/tree/index.ts +2 -1
- package/src/core/tree/pathTree.ts +16 -4
- package/src/core/tree/visitDelta.ts +6 -2
- package/src/core/tree/visitorUtils.ts +55 -19
- package/src/feature-libraries/changeAtomIdBTree.ts +37 -5
- package/src/feature-libraries/chunked-forest/basicChunk.ts +7 -1
- package/src/feature-libraries/chunked-forest/chunkTree.ts +6 -1
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +3 -1
- package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +1 -0
- package/src/feature-libraries/default-schema/defaultEditBuilder.ts +399 -127
- package/src/feature-libraries/default-schema/defaultFieldKinds.ts +13 -5
- package/src/feature-libraries/default-schema/index.ts +17 -5
- package/src/feature-libraries/default-schema/locationBasedEditBuilder.ts +188 -0
- package/src/feature-libraries/default-schema/mappedEditBuilder.ts +35 -9
- package/src/feature-libraries/deltaUtils.ts +6 -1
- package/src/feature-libraries/flex-tree/context.ts +17 -0
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +7 -8
- package/src/feature-libraries/flex-tree/lazyField.ts +68 -25
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +3 -1
- package/src/feature-libraries/index.ts +22 -8
- package/src/feature-libraries/mapTreeCursor.ts +2 -1
- package/src/feature-libraries/mitigatedChangeFamily.ts +3 -1
- package/src/feature-libraries/modular-schema/crossFieldQueries.ts +142 -44
- package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +32 -58
- package/src/feature-libraries/modular-schema/genericFieldKind.ts +6 -18
- package/src/feature-libraries/modular-schema/index.ts +16 -15
- package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +560 -351
- package/src/feature-libraries/modular-schema/modularChangeCodecV3.ts +760 -0
- package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +14 -1
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +2620 -782
- package/src/feature-libraries/modular-schema/modularChangeFormatV1.ts +1 -0
- package/src/feature-libraries/modular-schema/modularChangeFormatV3.ts +62 -0
- package/src/feature-libraries/modular-schema/modularChangeTypes.ts +58 -11
- package/src/feature-libraries/object-forest/objectForest.ts +3 -1
- package/src/feature-libraries/optional-field/index.ts +1 -3
- package/src/feature-libraries/optional-field/optionalField.ts +318 -562
- package/src/feature-libraries/optional-field/optionalFieldChangeFormatV3.ts +45 -0
- package/src/feature-libraries/optional-field/optionalFieldChangeTypes.ts +24 -38
- package/src/feature-libraries/optional-field/optionalFieldCodecV2.ts +89 -35
- package/src/feature-libraries/optional-field/optionalFieldCodecV3.ts +94 -0
- package/src/feature-libraries/optional-field/optionalFieldCodecs.ts +5 -1
- package/src/feature-libraries/sequence-field/compose.ts +139 -522
- package/src/feature-libraries/sequence-field/helperTypes.ts +34 -19
- package/src/feature-libraries/sequence-field/index.ts +0 -9
- package/src/feature-libraries/sequence-field/invert.ts +103 -228
- package/src/feature-libraries/sequence-field/markQueue.ts +2 -2
- package/src/feature-libraries/sequence-field/moveEffectTable.ts +8 -195
- package/src/feature-libraries/sequence-field/rebase.ts +169 -205
- package/src/feature-libraries/sequence-field/replaceRevisions.ts +25 -47
- package/src/feature-libraries/sequence-field/sequenceFieldChangeHandler.ts +0 -2
- package/src/feature-libraries/sequence-field/sequenceFieldCodecV2.ts +648 -228
- package/src/feature-libraries/sequence-field/sequenceFieldCodecV3.ts +56 -70
- package/src/feature-libraries/sequence-field/sequenceFieldEditor.ts +25 -27
- package/src/feature-libraries/sequence-field/sequenceFieldToDelta.ts +19 -129
- package/src/feature-libraries/sequence-field/types.ts +34 -64
- package/src/feature-libraries/sequence-field/utils.ts +139 -353
- package/src/index.ts +7 -0
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/index.ts +3 -2
- package/src/shared-tree/schematizeTree.ts +21 -8
- package/src/shared-tree/schematizingTreeView.ts +56 -70
- package/src/shared-tree/sharedTree.ts +20 -3
- package/src/shared-tree/sharedTreeChangeCodecs.ts +5 -1
- package/src/shared-tree/sharedTreeChangeEnricher.ts +2 -0
- package/src/shared-tree/sharedTreeChangeFamily.ts +15 -5
- package/src/shared-tree/sharedTreeEditBuilder.ts +39 -8
- package/src/shared-tree/treeCheckout.ts +95 -24
- package/src/shared-tree-core/branch.ts +13 -3
- package/src/shared-tree-core/editManager.ts +42 -28
- package/src/shared-tree-core/editManagerCodecs.ts +11 -1
- package/src/shared-tree-core/editManagerFormatCommons.ts +6 -0
- package/src/shared-tree-core/editManagerFormatV1toV4.ts +3 -1
- package/src/shared-tree-core/index.ts +2 -0
- package/src/shared-tree-core/messageCodecV1ToV4.ts +2 -1
- package/src/shared-tree-core/messageCodecs.ts +11 -1
- package/src/shared-tree-core/messageFormat.ts +6 -0
- package/src/shared-tree-core/messageFormatV1ToV4.ts +3 -1
- package/src/shared-tree-core/sharedTreeCore.ts +4 -1
- package/src/simple-tree/api/index.ts +3 -0
- package/src/simple-tree/api/schemaFactoryAlpha.ts +1 -1
- package/src/simple-tree/api/schemaFactoryBeta.ts +1 -1
- package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +4 -4
- package/src/simple-tree/api/snapshotCompatibilityChecker.ts +501 -0
- package/src/simple-tree/api/tree.ts +3 -1
- package/src/simple-tree/core/treeNodeKernel.ts +6 -2
- package/src/simple-tree/core/unhydratedFlexTree.ts +87 -36
- package/src/simple-tree/fieldSchema.ts +6 -4
- package/src/simple-tree/index.ts +5 -1
- package/src/simple-tree/node-kinds/array/arrayNode.ts +9 -7
- package/src/simple-tree/node-kinds/common.ts +2 -5
- package/src/simple-tree/node-kinds/map/mapNode.ts +4 -4
- package/src/simple-tree/node-kinds/object/objectNode.ts +26 -26
- package/src/simple-tree/node-kinds/record/recordNode.ts +10 -9
- package/src/simple-tree/prepareForInsertion.ts +343 -201
- package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +43 -15
- package/src/text/README.md +8 -0
- package/src/text/index.ts +6 -0
- package/src/text/textDomain.ts +199 -0
- package/src/treeFactory.ts +14 -4
- package/src/util/bTreeUtils.ts +33 -22
- package/src/util/index.ts +3 -0
- package/src/util/nestedMap.ts +12 -12
- package/src/util/rangeMap.ts +72 -18
- package/.eslintrc.cjs +0 -125
- package/assertTagging.config.mjs +0 -14
- package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
- package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
- package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -50
- package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
- package/docs/main/sequence-field/move-composition.md +0 -46
- package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
- package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
- package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -46
- package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
- package/src/feature-libraries/sequence-field/relevantRemovedRoots.ts +0 -57
|
@@ -27,12 +27,10 @@ import {
|
|
|
27
27
|
EditBuilder,
|
|
28
28
|
type FieldKey,
|
|
29
29
|
type FieldKindIdentifier,
|
|
30
|
-
type FieldUpPath,
|
|
31
30
|
type RevisionInfo,
|
|
32
31
|
type RevisionMetadataSource,
|
|
33
32
|
type RevisionTag,
|
|
34
33
|
type TaggedChange,
|
|
35
|
-
type UpPath,
|
|
36
34
|
makeDetachedNodeId,
|
|
37
35
|
revisionMetadataSourceFromInfo,
|
|
38
36
|
areEqualChangeAtomIds,
|
|
@@ -40,10 +38,18 @@ import {
|
|
|
40
38
|
areEqualChangeAtomIdOpts,
|
|
41
39
|
tagChange,
|
|
42
40
|
makeAnonChange,
|
|
43
|
-
newChangeAtomIdRangeMap,
|
|
44
41
|
type DeltaDetachedNodeChanges,
|
|
45
42
|
type DeltaDetachedNodeRename,
|
|
46
43
|
mapTaggedChange,
|
|
44
|
+
newChangeAtomIdRangeMap,
|
|
45
|
+
newChangeAtomIdTransform,
|
|
46
|
+
type ChangeAtomIdRangeMap,
|
|
47
|
+
offsetChangeAtomId,
|
|
48
|
+
type NormalizedUpPath,
|
|
49
|
+
type NormalizedFieldUpPath,
|
|
50
|
+
isDetachedUpPathRoot,
|
|
51
|
+
subtractChangeAtomIds,
|
|
52
|
+
makeChangeAtomId,
|
|
47
53
|
type RevisionReplacer,
|
|
48
54
|
} from "../../core/index.js";
|
|
49
55
|
import {
|
|
@@ -60,14 +66,19 @@ import {
|
|
|
60
66
|
type TupleBTree,
|
|
61
67
|
RangeMap,
|
|
62
68
|
balancedReduce,
|
|
69
|
+
type RangeQueryEntry,
|
|
70
|
+
type RangeQueryResultFragment,
|
|
63
71
|
} from "../../util/index.js";
|
|
64
72
|
import type { TreeChunk } from "../chunked-forest/index.js";
|
|
65
73
|
|
|
66
74
|
import {
|
|
67
|
-
type
|
|
75
|
+
type ComposeNodeManager,
|
|
68
76
|
type CrossFieldMap,
|
|
69
77
|
CrossFieldTarget,
|
|
70
|
-
|
|
78
|
+
type DetachedNodeEntry,
|
|
79
|
+
type InvertNodeManager,
|
|
80
|
+
type RebaseDetachedNodeEntry,
|
|
81
|
+
type RebaseNodeManager,
|
|
71
82
|
setInCrossFieldMap,
|
|
72
83
|
} from "./crossFieldQueries.js";
|
|
73
84
|
import {
|
|
@@ -81,23 +92,28 @@ import {
|
|
|
81
92
|
type CrossFieldKey,
|
|
82
93
|
type CrossFieldKeyRange,
|
|
83
94
|
type CrossFieldKeyTable,
|
|
95
|
+
type CrossFieldRangeTable,
|
|
84
96
|
type FieldChange,
|
|
85
97
|
type FieldChangeMap,
|
|
86
98
|
type FieldChangeset,
|
|
87
99
|
type FieldId,
|
|
88
100
|
type ModularChangeset,
|
|
89
|
-
|
|
101
|
+
newCrossFieldRangeTable,
|
|
90
102
|
type NoChangeConstraint,
|
|
91
103
|
type NodeChangeset,
|
|
92
104
|
type NodeId,
|
|
105
|
+
type NodeLocation,
|
|
106
|
+
type RebaseVersion,
|
|
107
|
+
type RootNodeTable,
|
|
93
108
|
} from "./modularChangeTypes.js";
|
|
94
109
|
import type { FlexFieldKind } from "./fieldKind.js";
|
|
110
|
+
import { lt } from "semver-ts";
|
|
95
111
|
import {
|
|
96
112
|
getFromChangeAtomIdMap,
|
|
113
|
+
rangeQueryChangeAtomIdMap,
|
|
97
114
|
setInChangeAtomIdMap,
|
|
98
115
|
type ChangeAtomIdBTree,
|
|
99
116
|
} from "../changeAtomIdBTree.js";
|
|
100
|
-
import { lt } from "semver-ts";
|
|
101
117
|
|
|
102
118
|
/**
|
|
103
119
|
* Implementation of ChangeFamily which delegates work in a given field to the appropriate FieldKind
|
|
@@ -183,14 +199,14 @@ export class ModularChangeFamily
|
|
|
183
199
|
}
|
|
184
200
|
|
|
185
201
|
public compose(changes: TaggedChange<ModularChangeset>[]): ModularChangeset {
|
|
186
|
-
const {
|
|
202
|
+
const { maxId } = getRevInfoFromTaggedChanges(changes);
|
|
187
203
|
const idState: IdAllocationState = { maxId };
|
|
188
204
|
|
|
189
205
|
const pairwiseDelegate = (
|
|
190
206
|
left: ModularChangeset,
|
|
191
207
|
right: ModularChangeset,
|
|
192
208
|
): ModularChangeset => {
|
|
193
|
-
return this.composePair(left, right,
|
|
209
|
+
return this.composePair(left, right, idState);
|
|
194
210
|
};
|
|
195
211
|
|
|
196
212
|
const innerChanges = changes.map((change) => change.change);
|
|
@@ -200,10 +216,11 @@ export class ModularChangeFamily
|
|
|
200
216
|
private composePair(
|
|
201
217
|
change1: ModularChangeset,
|
|
202
218
|
change2: ModularChangeset,
|
|
203
|
-
revInfos: RevisionInfo[],
|
|
204
219
|
idState: IdAllocationState,
|
|
205
220
|
): ModularChangeset {
|
|
206
|
-
const
|
|
221
|
+
const revInfos = composeRevInfos(change1.revisions, change2.revisions);
|
|
222
|
+
|
|
223
|
+
const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, rootNodes } =
|
|
207
224
|
this.composeAllFields(change1, change2, revInfos, idState);
|
|
208
225
|
|
|
209
226
|
const { allBuilds, allDestroys, allRefreshers } = composeBuildsDestroysAndRefreshers(
|
|
@@ -216,7 +233,7 @@ export class ModularChangeFamily
|
|
|
216
233
|
const noChangeConstraintOnRevert =
|
|
217
234
|
change1.noChangeConstraintOnRevert ?? change2.noChangeConstraintOnRevert;
|
|
218
235
|
|
|
219
|
-
|
|
236
|
+
const composed = makeModularChangeset({
|
|
220
237
|
fieldChanges,
|
|
221
238
|
nodeChanges,
|
|
222
239
|
nodeToParent,
|
|
@@ -224,18 +241,23 @@ export class ModularChangeFamily
|
|
|
224
241
|
crossFieldKeys,
|
|
225
242
|
maxId: idState.maxId,
|
|
226
243
|
revisions: revInfos,
|
|
227
|
-
|
|
228
|
-
noChangeConstraintOnRevert,
|
|
244
|
+
rootNodes,
|
|
229
245
|
builds: allBuilds,
|
|
230
246
|
destroys: allDestroys,
|
|
231
247
|
refreshers: allRefreshers,
|
|
248
|
+
noChangeConstraint,
|
|
249
|
+
noChangeConstraintOnRevert,
|
|
232
250
|
});
|
|
251
|
+
|
|
252
|
+
// XXX: This is an expensive assert which should be disabled before merging.
|
|
253
|
+
validateChangeset(composed, this.fieldKinds);
|
|
254
|
+
return composed;
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
private composeAllFields(
|
|
236
258
|
potentiallyConflictedChange1: ModularChangeset,
|
|
237
259
|
potentiallyConflictedChange2: ModularChangeset,
|
|
238
|
-
revInfos: RevisionInfo[],
|
|
260
|
+
revInfos: readonly RevisionInfo[],
|
|
239
261
|
idState: IdAllocationState,
|
|
240
262
|
): ModularChangesetContent {
|
|
241
263
|
// Our current cell ordering scheme in sequences depends on being able to rebase over a change with conflicts.
|
|
@@ -259,14 +281,38 @@ export class ModularChangeFamily
|
|
|
259
281
|
mergeTupleBTrees(change1.nodeChanges, change2.nodeChanges),
|
|
260
282
|
);
|
|
261
283
|
|
|
262
|
-
const composedNodeToParent: ChangeAtomIdBTree<
|
|
284
|
+
const composedNodeToParent: ChangeAtomIdBTree<NodeLocation> = brand(
|
|
263
285
|
mergeTupleBTrees(change1.nodeToParent, change2.nodeToParent),
|
|
264
286
|
);
|
|
265
287
|
const composedNodeAliases: ChangeAtomIdBTree<NodeId> = brand(
|
|
266
288
|
mergeTupleBTrees(change1.nodeAliases, change2.nodeAliases),
|
|
267
289
|
);
|
|
268
290
|
|
|
269
|
-
const
|
|
291
|
+
const pendingCompositions: PendingCompositions = {
|
|
292
|
+
nodeIdsToCompose: [],
|
|
293
|
+
affectedBaseFields: newTupleBTree(),
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const movedCrossFieldKeys: CrossFieldKeyTable = newCrossFieldRangeTable();
|
|
297
|
+
const removedCrossFieldKeys: CrossFieldRangeTable<boolean> = newCrossFieldRangeTable();
|
|
298
|
+
|
|
299
|
+
const composedRoots = composeRootTables(
|
|
300
|
+
change1,
|
|
301
|
+
change2,
|
|
302
|
+
composedNodeToParent,
|
|
303
|
+
movedCrossFieldKeys,
|
|
304
|
+
removedCrossFieldKeys,
|
|
305
|
+
pendingCompositions,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const crossFieldTable = newComposeTable(
|
|
309
|
+
change1,
|
|
310
|
+
change2,
|
|
311
|
+
composedRoots,
|
|
312
|
+
movedCrossFieldKeys,
|
|
313
|
+
removedCrossFieldKeys,
|
|
314
|
+
pendingCompositions,
|
|
315
|
+
);
|
|
270
316
|
|
|
271
317
|
const composedFields = this.composeFieldMaps(
|
|
272
318
|
change1.fieldChanges,
|
|
@@ -287,17 +333,32 @@ export class ModularChangeFamily
|
|
|
287
333
|
revisionMetadata,
|
|
288
334
|
);
|
|
289
335
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
)
|
|
336
|
+
for (const entry of crossFieldTable.renamesToDelete.entries()) {
|
|
337
|
+
deleteNodeRenameFrom(crossFieldTable.composedRootNodes, entry.start, entry.length);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
for (const [nodeId, location] of crossFieldTable.movedNodeToParent.entries()) {
|
|
341
|
+
// Moved nodes are from change2.
|
|
342
|
+
// If there is a corresponding node in change1, then composedNodeToParent will already have the correct entry,
|
|
343
|
+
// because the location of the node is the same in change1 and the composed change
|
|
344
|
+
// (since they have the same input context).
|
|
345
|
+
if (crossFieldTable.newToBaseNodeId.get(nodeId) === undefined) {
|
|
346
|
+
composedNodeToParent.set(nodeId, location);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
295
350
|
return {
|
|
296
351
|
fieldChanges: composedFields,
|
|
297
352
|
nodeChanges: composedNodeChanges,
|
|
298
353
|
nodeToParent: composedNodeToParent,
|
|
299
354
|
nodeAliases: composedNodeAliases,
|
|
300
|
-
crossFieldKeys:
|
|
355
|
+
crossFieldKeys: composeCrossFieldKeyTables(
|
|
356
|
+
change1.crossFieldKeys,
|
|
357
|
+
change2.crossFieldKeys,
|
|
358
|
+
crossFieldTable.movedCrossFieldKeys,
|
|
359
|
+
crossFieldTable.removedCrossFieldKeys,
|
|
360
|
+
),
|
|
361
|
+
rootNodes: composedRoots,
|
|
301
362
|
};
|
|
302
363
|
}
|
|
303
364
|
|
|
@@ -309,17 +370,16 @@ export class ModularChangeFamily
|
|
|
309
370
|
): void {
|
|
310
371
|
const context = crossFieldTable.fieldToContext.get(fieldChange);
|
|
311
372
|
assert(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
|
|
312
|
-
const {
|
|
373
|
+
const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
|
|
374
|
+
|
|
375
|
+
crossFieldTable.pendingCompositions.affectedBaseFields.delete(
|
|
376
|
+
fieldIdKeyFromFieldId(context.fieldId),
|
|
377
|
+
);
|
|
313
378
|
|
|
314
379
|
const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
|
|
315
380
|
const composeNodes = (child1: NodeId | undefined, child2: NodeId | undefined): NodeId => {
|
|
316
|
-
if (
|
|
317
|
-
child1
|
|
318
|
-
child2 !== undefined &&
|
|
319
|
-
getFromChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2) === undefined
|
|
320
|
-
) {
|
|
321
|
-
setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
|
|
322
|
-
crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
|
|
381
|
+
if (child1 !== undefined && child2 !== undefined) {
|
|
382
|
+
addNodesToCompose(crossFieldTable, child1, child2);
|
|
323
383
|
}
|
|
324
384
|
|
|
325
385
|
return child1 ?? child2 ?? fail(0xb22 /* Should not compose two undefined nodes */);
|
|
@@ -330,7 +390,7 @@ export class ModularChangeFamily
|
|
|
330
390
|
fieldChange2,
|
|
331
391
|
composeNodes,
|
|
332
392
|
genId,
|
|
333
|
-
new
|
|
393
|
+
new ComposeNodeManagerI(crossFieldTable, context.fieldId, false),
|
|
334
394
|
revisionMetadata,
|
|
335
395
|
);
|
|
336
396
|
composedChange.change = brand(amendedChange);
|
|
@@ -342,7 +402,7 @@ export class ModularChangeFamily
|
|
|
342
402
|
* - discovering that two node changesets refer to the same node (`nodeIdsToCompose`)
|
|
343
403
|
* - a previously composed field being invalidated by a cross field effect (`invalidatedFields`)
|
|
344
404
|
* - a field which was copied directly from an input changeset being invalidated by a cross field effect
|
|
345
|
-
* (`affectedBaseFields`
|
|
405
|
+
* (`affectedBaseFields`)
|
|
346
406
|
*
|
|
347
407
|
* Updating an element may invalidate further elements. This function runs until there is no more invalidation.
|
|
348
408
|
*/
|
|
@@ -350,72 +410,59 @@ export class ModularChangeFamily
|
|
|
350
410
|
table: ComposeTable,
|
|
351
411
|
composedFields: FieldChangeMap,
|
|
352
412
|
composedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
353
|
-
composedNodeToParent: ChangeAtomIdBTree<
|
|
413
|
+
composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
354
414
|
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
355
415
|
genId: IdAllocator,
|
|
356
416
|
metadata: RevisionMetadataSource,
|
|
357
417
|
): void {
|
|
358
418
|
const pending = table.pendingCompositions;
|
|
359
|
-
while (
|
|
360
|
-
|
|
361
|
-
pending.nodeIdsToCompose.length > 0 ||
|
|
362
|
-
pending.affectedBaseFields.length > 0 ||
|
|
363
|
-
pending.affectedNewFields.length > 0
|
|
364
|
-
) {
|
|
365
|
-
// Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
|
|
366
|
-
for (const [id1, id2] of pending.nodeIdsToCompose) {
|
|
367
|
-
this.composeNodesById(
|
|
368
|
-
table.baseChange.nodeChanges,
|
|
369
|
-
table.newChange.nodeChanges,
|
|
370
|
-
composedNodes,
|
|
371
|
-
composedNodeToParent,
|
|
372
|
-
nodeAliases,
|
|
373
|
-
id1,
|
|
374
|
-
id2,
|
|
375
|
-
genId,
|
|
376
|
-
table,
|
|
377
|
-
metadata,
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
pending.nodeIdsToCompose.length = 0;
|
|
382
|
-
|
|
383
|
-
this.composeAffectedFields(
|
|
419
|
+
while (pending.nodeIdsToCompose.length > 0 || pending.affectedBaseFields.length > 0) {
|
|
420
|
+
this.processPendingNodeCompositions(
|
|
384
421
|
table,
|
|
385
|
-
table.baseChange,
|
|
386
|
-
true,
|
|
387
|
-
pending.affectedBaseFields,
|
|
388
|
-
composedFields,
|
|
389
422
|
composedNodes,
|
|
423
|
+
composedNodeToParent,
|
|
424
|
+
nodeAliases,
|
|
390
425
|
genId,
|
|
391
426
|
metadata,
|
|
392
427
|
);
|
|
393
428
|
|
|
394
429
|
this.composeAffectedFields(
|
|
395
430
|
table,
|
|
396
|
-
table.
|
|
397
|
-
|
|
398
|
-
pending.affectedNewFields,
|
|
431
|
+
table.baseChange,
|
|
432
|
+
pending.affectedBaseFields,
|
|
399
433
|
composedFields,
|
|
400
434
|
composedNodes,
|
|
401
435
|
genId,
|
|
402
436
|
metadata,
|
|
403
437
|
);
|
|
404
|
-
|
|
405
|
-
this.processInvalidatedCompositions(table, genId, metadata);
|
|
406
438
|
}
|
|
407
439
|
}
|
|
408
440
|
|
|
409
|
-
private
|
|
441
|
+
private processPendingNodeCompositions(
|
|
410
442
|
table: ComposeTable,
|
|
443
|
+
composedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
444
|
+
composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
445
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
411
446
|
genId: IdAllocator,
|
|
412
447
|
metadata: RevisionMetadataSource,
|
|
413
448
|
): void {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
449
|
+
// Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
|
|
450
|
+
for (const [id1, id2] of table.pendingCompositions.nodeIdsToCompose) {
|
|
451
|
+
this.composeNodesById(
|
|
452
|
+
table.baseChange,
|
|
453
|
+
table.newChange,
|
|
454
|
+
composedNodes,
|
|
455
|
+
composedNodeToParent,
|
|
456
|
+
nodeAliases,
|
|
457
|
+
id1,
|
|
458
|
+
id2,
|
|
459
|
+
genId,
|
|
460
|
+
table,
|
|
461
|
+
metadata,
|
|
462
|
+
);
|
|
418
463
|
}
|
|
464
|
+
|
|
465
|
+
table.pendingCompositions.nodeIdsToCompose.length = 0;
|
|
419
466
|
}
|
|
420
467
|
|
|
421
468
|
/**
|
|
@@ -432,63 +479,79 @@ export class ModularChangeFamily
|
|
|
432
479
|
private composeAffectedFields(
|
|
433
480
|
table: ComposeTable,
|
|
434
481
|
change: ModularChangeset,
|
|
435
|
-
areBaseFields: boolean,
|
|
436
482
|
affectedFields: BTree<FieldIdKey, true>,
|
|
437
483
|
composedFields: FieldChangeMap,
|
|
438
484
|
composedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
439
485
|
genId: IdAllocator,
|
|
440
486
|
metadata: RevisionMetadataSource,
|
|
441
487
|
): void {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
488
|
+
const fieldsToProcess = affectedFields.clone();
|
|
489
|
+
affectedFields.clear();
|
|
490
|
+
|
|
491
|
+
for (const fieldIdKey of fieldsToProcess.keys()) {
|
|
492
|
+
const fieldId = fieldIdFromFieldIdKey(fieldIdKey);
|
|
493
|
+
const fieldChange = fieldChangeFromId(change, fieldId);
|
|
445
494
|
|
|
446
495
|
if (
|
|
447
496
|
table.fieldToContext.has(fieldChange) ||
|
|
448
497
|
table.newFieldToBaseField.has(fieldChange)
|
|
449
498
|
) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
499
|
+
this.composeInvalidatedField(fieldChange, table, genId, metadata);
|
|
500
|
+
} else {
|
|
501
|
+
this.composeFieldWithNoNewChange(
|
|
502
|
+
table,
|
|
503
|
+
fieldChange,
|
|
504
|
+
fieldId,
|
|
505
|
+
composedFields,
|
|
506
|
+
composedNodes,
|
|
507
|
+
genId,
|
|
508
|
+
metadata,
|
|
509
|
+
);
|
|
454
510
|
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
455
513
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
table,
|
|
467
|
-
metadata,
|
|
468
|
-
);
|
|
514
|
+
private composeFieldWithNoNewChange(
|
|
515
|
+
table: ComposeTable,
|
|
516
|
+
baseFieldChange: FieldChange,
|
|
517
|
+
fieldId: FieldId,
|
|
518
|
+
composedFields: FieldChangeMap,
|
|
519
|
+
composedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
520
|
+
genId: IdAllocator,
|
|
521
|
+
metadata: RevisionMetadataSource,
|
|
522
|
+
): void {
|
|
523
|
+
const emptyChange = this.createEmptyFieldChange(baseFieldChange.fieldKind);
|
|
469
524
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
525
|
+
const composedField = this.composeFieldChanges(
|
|
526
|
+
fieldId,
|
|
527
|
+
baseFieldChange,
|
|
528
|
+
emptyChange,
|
|
529
|
+
genId,
|
|
530
|
+
table,
|
|
531
|
+
metadata,
|
|
532
|
+
);
|
|
474
533
|
|
|
475
|
-
|
|
476
|
-
|
|
534
|
+
if (fieldId.nodeId === undefined) {
|
|
535
|
+
composedFields.set(fieldId.field, composedField);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
477
538
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
539
|
+
const nodeId = normalizeNodeId(
|
|
540
|
+
getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId,
|
|
541
|
+
table.baseChange.nodeAliases,
|
|
542
|
+
);
|
|
483
543
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
544
|
+
// We clone the node changeset before mutating it, as it may be from one of the input changesets.
|
|
545
|
+
const nodeChangeset: Mutable<NodeChangeset> = cloneNodeChangeset(
|
|
546
|
+
nodeChangeFromId(composedNodes, table.baseChange.nodeAliases, nodeId),
|
|
547
|
+
);
|
|
548
|
+
setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
|
|
487
549
|
|
|
488
|
-
|
|
550
|
+
if (nodeChangeset.fieldChanges === undefined) {
|
|
551
|
+
nodeChangeset.fieldChanges = new Map();
|
|
489
552
|
}
|
|
490
553
|
|
|
491
|
-
|
|
554
|
+
nodeChangeset.fieldChanges.set(fieldId.field, composedField);
|
|
492
555
|
}
|
|
493
556
|
|
|
494
557
|
private composeFieldMaps(
|
|
@@ -507,6 +570,18 @@ export class ModularChangeFamily
|
|
|
507
570
|
for (const [field, fieldChange1] of change1) {
|
|
508
571
|
const fieldId: FieldId = { nodeId: parentId, field };
|
|
509
572
|
const fieldChange2 = change2.get(field);
|
|
573
|
+
|
|
574
|
+
const cachedComposedFieldChange =
|
|
575
|
+
crossFieldTable.fieldToContext.get(fieldChange1)?.composedChange;
|
|
576
|
+
|
|
577
|
+
if (fieldChange2 === undefined && cachedComposedFieldChange !== undefined) {
|
|
578
|
+
// This can happen if the field was previous processed in `composeFieldWithNoNewChange`.
|
|
579
|
+
// If `change2` does not have a change for this field, then without this check we would
|
|
580
|
+
// lose the composed field change and instead simply have `change1`'s change.
|
|
581
|
+
composedFields.set(field, cachedComposedFieldChange);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
|
|
510
585
|
const composedField =
|
|
511
586
|
fieldChange2 === undefined
|
|
512
587
|
? fieldChange1
|
|
@@ -538,7 +613,7 @@ export class ModularChangeFamily
|
|
|
538
613
|
* will be added to `crossFieldTable.pendingCompositions.nodeIdsToCompose`.
|
|
539
614
|
*
|
|
540
615
|
* Any fields which had cross-field information sent to them as part of this field composition
|
|
541
|
-
* will be added to
|
|
616
|
+
* will be added to `affectedBaseFields` in `crossFieldTable.pendingCompositions`.
|
|
542
617
|
*
|
|
543
618
|
* Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
|
|
544
619
|
*/
|
|
@@ -557,15 +632,14 @@ export class ModularChangeFamily
|
|
|
557
632
|
change2: change2Normalized,
|
|
558
633
|
} = this.normalizeFieldChanges(change1, change2);
|
|
559
634
|
|
|
560
|
-
const manager = new
|
|
635
|
+
const manager = new ComposeNodeManagerI(crossFieldTable, fieldId);
|
|
561
636
|
|
|
562
637
|
const composedChange = changeHandler.rebaser.compose(
|
|
563
638
|
change1Normalized,
|
|
564
639
|
change2Normalized,
|
|
565
640
|
(child1, child2) => {
|
|
566
641
|
if (child1 !== undefined && child2 !== undefined) {
|
|
567
|
-
|
|
568
|
-
crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
|
|
642
|
+
addNodesToCompose(crossFieldTable, child1, child2);
|
|
569
643
|
}
|
|
570
644
|
return child1 ?? child2 ?? fail(0xb23 /* Should not compose two undefined nodes */);
|
|
571
645
|
},
|
|
@@ -591,19 +665,19 @@ export class ModularChangeFamily
|
|
|
591
665
|
}
|
|
592
666
|
|
|
593
667
|
private composeNodesById(
|
|
594
|
-
|
|
595
|
-
|
|
668
|
+
change1: ModularChangeset,
|
|
669
|
+
change2: ModularChangeset,
|
|
596
670
|
composedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
597
|
-
composedNodeToParent: ChangeAtomIdBTree<
|
|
598
|
-
|
|
671
|
+
composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
672
|
+
composedAliases: ChangeAtomIdBTree<NodeId>,
|
|
599
673
|
id1: NodeId,
|
|
600
674
|
id2: NodeId,
|
|
601
675
|
idAllocator: IdAllocator,
|
|
602
676
|
crossFieldTable: ComposeTable,
|
|
603
677
|
revisionMetadata: RevisionMetadataSource,
|
|
604
678
|
): void {
|
|
605
|
-
const nodeChangeset1 = nodeChangeFromId(
|
|
606
|
-
const nodeChangeset2 = nodeChangeFromId(
|
|
679
|
+
const nodeChangeset1 = nodeChangeFromId(change1.nodeChanges, change1.nodeAliases, id1);
|
|
680
|
+
const nodeChangeset2 = nodeChangeFromId(change2.nodeChanges, change2.nodeAliases, id2);
|
|
607
681
|
const composedNodeChangeset = this.composeNodeChanges(
|
|
608
682
|
id1,
|
|
609
683
|
nodeChangeset1,
|
|
@@ -618,13 +692,11 @@ export class ModularChangeFamily
|
|
|
618
692
|
if (!areEqualChangeAtomIds(id1, id2)) {
|
|
619
693
|
composedNodes.delete([id2.revision, id2.localId]);
|
|
620
694
|
composedNodeToParent.delete([id2.revision, id2.localId]);
|
|
621
|
-
setInChangeAtomIdMap(
|
|
695
|
+
setInChangeAtomIdMap(composedAliases, id2, id1);
|
|
622
696
|
|
|
623
697
|
// We need to delete id1 to avoid forming a cycle in case id1 already had an alias.
|
|
624
|
-
|
|
698
|
+
composedAliases.delete([id1.revision, id1.localId]);
|
|
625
699
|
}
|
|
626
|
-
|
|
627
|
-
crossFieldTable.composedNodes.add(composedNodeChangeset);
|
|
628
700
|
}
|
|
629
701
|
|
|
630
702
|
private composeNodeChanges(
|
|
@@ -659,7 +731,7 @@ export class ModularChangeFamily
|
|
|
659
731
|
revisionMetadata,
|
|
660
732
|
);
|
|
661
733
|
|
|
662
|
-
const composedNodeChange: NodeChangeset = {};
|
|
734
|
+
const composedNodeChange: Mutable<NodeChangeset> = {};
|
|
663
735
|
|
|
664
736
|
if (composedFieldChanges.size > 0) {
|
|
665
737
|
composedNodeChange.fieldChanges = composedFieldChanges;
|
|
@@ -714,9 +786,14 @@ export class ModularChangeFamily
|
|
|
714
786
|
const genId: IdAllocator = idAllocatorFromMaxId(change.change.maxId ?? -1);
|
|
715
787
|
|
|
716
788
|
const crossFieldTable: InvertTable = {
|
|
717
|
-
|
|
789
|
+
change: change.change,
|
|
790
|
+
entries: newChangeAtomIdRangeMap(),
|
|
718
791
|
originalFieldToContext: new Map(),
|
|
792
|
+
invertRevision: revisionForInvert,
|
|
719
793
|
invertedNodeToParent: brand(change.change.nodeToParent.clone()),
|
|
794
|
+
invalidatedFields: new Set(),
|
|
795
|
+
invertedRoots: invertRootTable(change.change, isRollback),
|
|
796
|
+
attachToDetachId: newChangeAtomIdTransform(),
|
|
720
797
|
};
|
|
721
798
|
const { revInfos: oldRevInfos } = getRevInfoFromTaggedChanges([change]);
|
|
722
799
|
const revisionMetadata = revisionMetadataSourceFromInfo(oldRevInfos);
|
|
@@ -757,7 +834,7 @@ export class ModularChangeFamily
|
|
|
757
834
|
context !== undefined,
|
|
758
835
|
0x851 /* Should have context for every invalidated field */,
|
|
759
836
|
);
|
|
760
|
-
const { invertedField
|
|
837
|
+
const { invertedField } = context;
|
|
761
838
|
|
|
762
839
|
const amendedChange = getChangeHandler(
|
|
763
840
|
this.fieldKinds,
|
|
@@ -767,7 +844,7 @@ export class ModularChangeFamily
|
|
|
767
844
|
isRollback,
|
|
768
845
|
genId,
|
|
769
846
|
revisionForInvert,
|
|
770
|
-
new
|
|
847
|
+
new InvertNodeManagerI(crossFieldTable, context.fieldId),
|
|
771
848
|
revisionMetadata,
|
|
772
849
|
);
|
|
773
850
|
invertedField.change = brand(amendedChange);
|
|
@@ -776,10 +853,13 @@ export class ModularChangeFamily
|
|
|
776
853
|
|
|
777
854
|
const crossFieldKeys = this.makeCrossFieldKeyTable(invertedFields, invertedNodes);
|
|
778
855
|
|
|
856
|
+
this.processInvertRenames(crossFieldTable);
|
|
857
|
+
|
|
779
858
|
return makeModularChangeset({
|
|
780
859
|
fieldChanges: invertedFields,
|
|
781
860
|
nodeChanges: invertedNodes,
|
|
782
861
|
nodeToParent: crossFieldTable.invertedNodeToParent,
|
|
862
|
+
rootNodes: crossFieldTable.invertedRoots,
|
|
783
863
|
nodeAliases: change.change.nodeAliases,
|
|
784
864
|
crossFieldKeys,
|
|
785
865
|
maxId: genId.getMaxId(),
|
|
@@ -805,7 +885,7 @@ export class ModularChangeFamily
|
|
|
805
885
|
|
|
806
886
|
for (const [field, fieldChange] of changes) {
|
|
807
887
|
const fieldId = { nodeId: parentId, field };
|
|
808
|
-
const manager = new
|
|
888
|
+
const manager = new InvertNodeManagerI(crossFieldTable, fieldId);
|
|
809
889
|
const invertedChange = getChangeHandler(
|
|
810
890
|
this.fieldKinds,
|
|
811
891
|
fieldChange.fieldKind,
|
|
@@ -842,7 +922,7 @@ export class ModularChangeFamily
|
|
|
842
922
|
revisionMetadata: RevisionMetadataSource,
|
|
843
923
|
revisionForInvert: RevisionTag,
|
|
844
924
|
): NodeChangeset {
|
|
845
|
-
const inverse: NodeChangeset = {};
|
|
925
|
+
const inverse: Mutable<NodeChangeset> = {};
|
|
846
926
|
|
|
847
927
|
// If the node has a constraint, it should be inverted to a node-exist-on-revert constraint. This ensure that if
|
|
848
928
|
// the inverse is inverted again, the original input constraint will be restored.
|
|
@@ -872,6 +952,17 @@ export class ModularChangeFamily
|
|
|
872
952
|
return inverse;
|
|
873
953
|
}
|
|
874
954
|
|
|
955
|
+
private processInvertRenames(table: InvertTable): void {
|
|
956
|
+
for (const {
|
|
957
|
+
start: newAttachId,
|
|
958
|
+
value: originalDetachId,
|
|
959
|
+
length,
|
|
960
|
+
} of table.attachToDetachId.entries()) {
|
|
961
|
+
// Note that the detach location is already set in `invertDetach`.
|
|
962
|
+
addNodeRename(table.invertedRoots, originalDetachId, newAttachId, length, undefined);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
875
966
|
public rebase(
|
|
876
967
|
taggedChange: TaggedChange<ModularChangeset>,
|
|
877
968
|
potentiallyConflictedOver: TaggedChange<ModularChangeset>,
|
|
@@ -891,17 +982,41 @@ export class ModularChangeFamily
|
|
|
891
982
|
const idState: IdAllocationState = { maxId };
|
|
892
983
|
const genId: IdAllocator = idAllocatorFromState(idState);
|
|
893
984
|
|
|
985
|
+
const affectedBaseFields: TupleBTree<FieldIdKey, boolean> = newTupleBTree();
|
|
986
|
+
const nodesToRebase: [newChangeset: NodeId, baseChangeset: NodeId][] = [];
|
|
987
|
+
|
|
988
|
+
const rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation> = brand(
|
|
989
|
+
change.nodeToParent.clone(),
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
const rebaseVersion = Math.max(
|
|
993
|
+
change.rebaseVersion,
|
|
994
|
+
over.change.rebaseVersion,
|
|
995
|
+
) as RebaseVersion;
|
|
996
|
+
|
|
997
|
+
const rebasedRootNodes = rebaseRoots(
|
|
998
|
+
change,
|
|
999
|
+
over.change,
|
|
1000
|
+
affectedBaseFields,
|
|
1001
|
+
nodesToRebase,
|
|
1002
|
+
rebasedNodeToParent,
|
|
1003
|
+
rebaseVersion,
|
|
1004
|
+
);
|
|
894
1005
|
const crossFieldTable: RebaseTable = {
|
|
895
|
-
|
|
1006
|
+
rebaseVersion,
|
|
1007
|
+
entries: newDetachedEntryMap(),
|
|
896
1008
|
newChange: change,
|
|
897
1009
|
baseChange: over.change,
|
|
898
1010
|
baseFieldToContext: new Map(),
|
|
1011
|
+
baseRoots: over.change.rootNodes,
|
|
1012
|
+
rebasedRootNodes,
|
|
899
1013
|
baseToRebasedNodeId: newTupleBTree(),
|
|
900
1014
|
rebasedFields: new Set(),
|
|
901
|
-
rebasedNodeToParent
|
|
902
|
-
|
|
1015
|
+
rebasedNodeToParent,
|
|
1016
|
+
rebasedDetachLocations: newChangeAtomIdRangeMap(),
|
|
1017
|
+
movedDetaches: newChangeAtomIdRangeMap(),
|
|
903
1018
|
nodeIdPairs: [],
|
|
904
|
-
affectedBaseFields
|
|
1019
|
+
affectedBaseFields,
|
|
905
1020
|
fieldsWithUnattachedChild: new Set(),
|
|
906
1021
|
};
|
|
907
1022
|
|
|
@@ -917,13 +1032,14 @@ export class ModularChangeFamily
|
|
|
917
1032
|
const rebasedNodes: ChangeAtomIdBTree<NodeChangeset> = brand(change.nodeChanges.clone());
|
|
918
1033
|
|
|
919
1034
|
const rebasedFields = this.rebaseIntersectingFields(
|
|
1035
|
+
nodesToRebase,
|
|
920
1036
|
crossFieldTable,
|
|
921
1037
|
rebasedNodes,
|
|
922
1038
|
genId,
|
|
923
1039
|
rebaseMetadata,
|
|
924
1040
|
);
|
|
925
1041
|
|
|
926
|
-
this.
|
|
1042
|
+
this.rebaseInvalidatedFields(
|
|
927
1043
|
rebasedFields,
|
|
928
1044
|
rebasedNodes,
|
|
929
1045
|
crossFieldTable,
|
|
@@ -931,6 +1047,8 @@ export class ModularChangeFamily
|
|
|
931
1047
|
genId,
|
|
932
1048
|
);
|
|
933
1049
|
|
|
1050
|
+
fixupRebasedDetachLocations(crossFieldTable);
|
|
1051
|
+
|
|
934
1052
|
const constraintState = newConstraintState(change.constraintViolationCount ?? 0);
|
|
935
1053
|
const revertConstraintState = newConstraintState(
|
|
936
1054
|
change.constraintViolationCountOnRevert ?? 0,
|
|
@@ -942,21 +1060,52 @@ export class ModularChangeFamily
|
|
|
942
1060
|
constraintState.violationCount += 1;
|
|
943
1061
|
}
|
|
944
1062
|
|
|
945
|
-
this.
|
|
1063
|
+
this.updateConstraints(
|
|
946
1064
|
rebasedFields,
|
|
947
|
-
|
|
948
|
-
|
|
1065
|
+
rebasedNodes,
|
|
1066
|
+
rebasedRootNodes,
|
|
949
1067
|
constraintState,
|
|
950
1068
|
revertConstraintState,
|
|
951
|
-
|
|
1069
|
+
);
|
|
1070
|
+
|
|
1071
|
+
const fieldsWithRootMoves = getFieldsWithRootMoves(
|
|
1072
|
+
crossFieldTable.rebasedRootNodes,
|
|
1073
|
+
change.nodeAliases,
|
|
1074
|
+
);
|
|
1075
|
+
|
|
1076
|
+
const fieldToRootChanges = getFieldToRootChanges(
|
|
1077
|
+
crossFieldTable.rebasedRootNodes,
|
|
1078
|
+
change.nodeAliases,
|
|
952
1079
|
);
|
|
953
1080
|
|
|
954
1081
|
const rebased = makeModularChangeset({
|
|
955
|
-
fieldChanges: this.pruneFieldMap(
|
|
1082
|
+
fieldChanges: this.pruneFieldMap(
|
|
1083
|
+
rebasedFields,
|
|
1084
|
+
undefined,
|
|
1085
|
+
rebasedNodes,
|
|
1086
|
+
crossFieldTable.rebasedNodeToParent,
|
|
1087
|
+
change.nodeAliases,
|
|
1088
|
+
crossFieldTable.rebasedRootNodes,
|
|
1089
|
+
fieldsWithRootMoves,
|
|
1090
|
+
fieldToRootChanges,
|
|
1091
|
+
),
|
|
956
1092
|
nodeChanges: rebasedNodes,
|
|
957
1093
|
nodeToParent: crossFieldTable.rebasedNodeToParent,
|
|
1094
|
+
rootNodes: this.pruneRoots(
|
|
1095
|
+
crossFieldTable.rebasedRootNodes,
|
|
1096
|
+
rebasedNodes,
|
|
1097
|
+
crossFieldTable.rebasedNodeToParent,
|
|
1098
|
+
change.nodeAliases,
|
|
1099
|
+
fieldsWithRootMoves,
|
|
1100
|
+
fieldToRootChanges,
|
|
1101
|
+
),
|
|
1102
|
+
// TODO: Do we need to include aliases for node changesets added during rebasing?
|
|
958
1103
|
nodeAliases: change.nodeAliases,
|
|
959
|
-
crossFieldKeys:
|
|
1104
|
+
crossFieldKeys: rebaseCrossFieldKeys(
|
|
1105
|
+
change.crossFieldKeys,
|
|
1106
|
+
crossFieldTable.movedDetaches,
|
|
1107
|
+
crossFieldTable.rebasedDetachLocations,
|
|
1108
|
+
),
|
|
960
1109
|
maxId: idState.maxId,
|
|
961
1110
|
revisions: change.revisions,
|
|
962
1111
|
constraintViolationCount: constraintState.violationCount,
|
|
@@ -966,14 +1115,18 @@ export class ModularChangeFamily
|
|
|
966
1115
|
builds: change.builds,
|
|
967
1116
|
destroys: change.destroys,
|
|
968
1117
|
refreshers: change.refreshers,
|
|
1118
|
+
rebaseVersion,
|
|
969
1119
|
});
|
|
970
1120
|
|
|
1121
|
+
// XXX: This is an expensive assert which should be disabled before merging.
|
|
1122
|
+
validateChangeset(rebased, this.fieldKinds);
|
|
971
1123
|
return rebased;
|
|
972
1124
|
}
|
|
973
1125
|
|
|
974
1126
|
// This performs a first pass on all fields which have both new and base changes.
|
|
975
1127
|
// TODO: Can we also handle additional passes in this method?
|
|
976
1128
|
private rebaseIntersectingFields(
|
|
1129
|
+
rootChanges: [newChangeset: NodeId, baseChangeset: NodeId][],
|
|
977
1130
|
crossFieldTable: RebaseTable,
|
|
978
1131
|
rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
979
1132
|
genId: IdAllocator,
|
|
@@ -990,6 +1143,18 @@ export class ModularChangeFamily
|
|
|
990
1143
|
metadata,
|
|
991
1144
|
);
|
|
992
1145
|
|
|
1146
|
+
for (const [newChildChange, baseChildChange] of rootChanges) {
|
|
1147
|
+
const rebasedNode = this.rebaseNodeChange(
|
|
1148
|
+
newChildChange,
|
|
1149
|
+
baseChildChange,
|
|
1150
|
+
genId,
|
|
1151
|
+
crossFieldTable,
|
|
1152
|
+
metadata,
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
setInChangeAtomIdMap(rebasedNodes, newChildChange, rebasedNode);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
993
1158
|
// This loop processes all fields which have both base and new changes.
|
|
994
1159
|
// Note that the call to `rebaseNodeChange` can add entries to `crossFieldTable.nodeIdPairs`.
|
|
995
1160
|
for (const [newId, baseId, _attachState] of crossFieldTable.nodeIdPairs) {
|
|
@@ -1007,146 +1172,150 @@ export class ModularChangeFamily
|
|
|
1007
1172
|
return rebasedFields;
|
|
1008
1173
|
}
|
|
1009
1174
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1175
|
+
private rebaseFieldWithoutNewChanges(
|
|
1176
|
+
baseFieldChange: FieldChange,
|
|
1177
|
+
baseFieldId: FieldId,
|
|
1178
|
+
crossFieldTable: RebaseTable,
|
|
1012
1179
|
rebasedFields: FieldChangeMap,
|
|
1013
1180
|
rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1014
|
-
crossFieldTable: RebaseTable,
|
|
1015
1181
|
genId: IdAllocator,
|
|
1016
1182
|
metadata: RebaseRevisionMetadata,
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* The ID of a node in `baseFieldChange` which should be included in the rebased field change.
|
|
1186
|
+
*/
|
|
1187
|
+
baseNodeId?: NodeId,
|
|
1017
1188
|
): void {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1189
|
+
// This field has no changes in the new changeset, otherwise it would have been added to
|
|
1190
|
+
// `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
|
|
1191
|
+
const rebaseChild = (
|
|
1192
|
+
child: NodeId | undefined,
|
|
1193
|
+
baseChild: NodeId | undefined,
|
|
1194
|
+
stateChange: NodeAttachState | undefined,
|
|
1195
|
+
): NodeId | undefined => {
|
|
1196
|
+
assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
|
|
1197
|
+
if (baseChild === undefined || baseNodeId === undefined) {
|
|
1198
|
+
return undefined;
|
|
1199
|
+
}
|
|
1024
1200
|
|
|
1025
|
-
|
|
1026
|
-
baseChange.
|
|
1027
|
-
baseChange.nodeChanges,
|
|
1201
|
+
return areEqualChangeAtomIds(
|
|
1202
|
+
normalizeNodeId(baseChild, crossFieldTable.baseChange.nodeAliases),
|
|
1028
1203
|
baseNodeId,
|
|
1029
|
-
)
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
0x9c2 /* Cross field key registered for empty field */,
|
|
1034
|
-
);
|
|
1035
|
-
if (crossFieldTable.baseFieldToContext.has(baseFieldChange)) {
|
|
1036
|
-
// This field has already been processed because there were changes to rebase.
|
|
1037
|
-
continue;
|
|
1038
|
-
}
|
|
1204
|
+
)
|
|
1205
|
+
? baseNodeId
|
|
1206
|
+
: undefined;
|
|
1207
|
+
};
|
|
1039
1208
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
stateChange: NodeAttachState | undefined,
|
|
1046
|
-
): NodeId | undefined => {
|
|
1047
|
-
assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
|
|
1048
|
-
return undefined;
|
|
1049
|
-
};
|
|
1209
|
+
const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
|
|
1210
|
+
const fieldChange: FieldChange = {
|
|
1211
|
+
...baseFieldChange,
|
|
1212
|
+
change: brand(handler.createEmpty()),
|
|
1213
|
+
};
|
|
1050
1214
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
};
|
|
1215
|
+
const rebasedNodeId =
|
|
1216
|
+
baseFieldId.nodeId === undefined
|
|
1217
|
+
? undefined
|
|
1218
|
+
: rebasedNodeIdFromBaseNodeId(crossFieldTable, baseFieldId.nodeId);
|
|
1056
1219
|
|
|
1057
|
-
|
|
1058
|
-
baseNodeId === undefined
|
|
1059
|
-
? undefined
|
|
1060
|
-
: rebasedNodeIdFromBaseNodeId(crossFieldTable, baseNodeId);
|
|
1220
|
+
const fieldId: FieldId = { nodeId: rebasedNodeId, field: baseFieldId.field };
|
|
1061
1221
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1222
|
+
const rebasedField: unknown = handler.rebaser.rebase(
|
|
1223
|
+
fieldChange.change,
|
|
1224
|
+
baseFieldChange.change,
|
|
1225
|
+
rebaseChild,
|
|
1226
|
+
genId,
|
|
1227
|
+
new RebaseNodeManagerI(crossFieldTable, fieldId),
|
|
1228
|
+
metadata,
|
|
1229
|
+
crossFieldTable.rebaseVersion,
|
|
1230
|
+
);
|
|
1071
1231
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1232
|
+
const rebasedFieldChange: FieldChange = {
|
|
1233
|
+
...baseFieldChange,
|
|
1234
|
+
change: brand(rebasedField),
|
|
1235
|
+
};
|
|
1076
1236
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
});
|
|
1085
|
-
crossFieldTable.rebasedFields.add(rebasedFieldChange);
|
|
1237
|
+
const context: RebaseFieldContext = {
|
|
1238
|
+
newChange: fieldChange,
|
|
1239
|
+
baseChange: baseFieldChange,
|
|
1240
|
+
rebasedChange: rebasedFieldChange,
|
|
1241
|
+
fieldId,
|
|
1242
|
+
baseNodeIds: newTupleBTree(),
|
|
1243
|
+
};
|
|
1086
1244
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
rebasedNodes,
|
|
1090
|
-
crossFieldTable,
|
|
1091
|
-
rebasedFieldChange,
|
|
1092
|
-
fieldId,
|
|
1093
|
-
genId,
|
|
1094
|
-
metadata,
|
|
1095
|
-
);
|
|
1245
|
+
if (baseNodeId !== undefined) {
|
|
1246
|
+
setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true);
|
|
1096
1247
|
}
|
|
1097
|
-
}
|
|
1098
1248
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
idAllocator: IdAllocator,
|
|
1105
|
-
): void {
|
|
1106
|
-
this.rebaseFieldsWithoutNewChanges(
|
|
1249
|
+
crossFieldTable.baseFieldToContext.set(baseFieldChange, context);
|
|
1250
|
+
|
|
1251
|
+
crossFieldTable.rebasedFields.add(rebasedFieldChange);
|
|
1252
|
+
|
|
1253
|
+
this.attachRebasedField(
|
|
1107
1254
|
rebasedFields,
|
|
1108
1255
|
rebasedNodes,
|
|
1109
|
-
|
|
1110
|
-
|
|
1256
|
+
crossFieldTable,
|
|
1257
|
+
rebasedFieldChange,
|
|
1258
|
+
fieldId,
|
|
1259
|
+
genId,
|
|
1111
1260
|
metadata,
|
|
1112
1261
|
);
|
|
1113
|
-
|
|
1114
|
-
this.rebaseFieldsWithUnattachedChild(table, metadata, idAllocator);
|
|
1115
|
-
this.rebaseInvalidatedFields(table, metadata, idAllocator);
|
|
1116
1262
|
}
|
|
1117
1263
|
|
|
1118
1264
|
private rebaseInvalidatedFields(
|
|
1265
|
+
rebasedFields: FieldChangeMap,
|
|
1266
|
+
rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1119
1267
|
crossFieldTable: RebaseTable,
|
|
1120
1268
|
rebaseMetadata: RebaseRevisionMetadata,
|
|
1121
1269
|
genId: IdAllocator,
|
|
1122
1270
|
): void {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1271
|
+
while (crossFieldTable.affectedBaseFields.size > 0) {
|
|
1272
|
+
const baseFields = crossFieldTable.affectedBaseFields.clone();
|
|
1273
|
+
crossFieldTable.affectedBaseFields.clear();
|
|
1274
|
+
|
|
1275
|
+
for (const baseFieldIdKey of baseFields.keys()) {
|
|
1276
|
+
const baseFieldId = normalizeFieldId(
|
|
1277
|
+
fieldIdFromFieldIdKey(baseFieldIdKey),
|
|
1278
|
+
crossFieldTable.baseChange.nodeAliases,
|
|
1279
|
+
);
|
|
1129
1280
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1281
|
+
const baseField = fieldChangeFromId(crossFieldTable.baseChange, baseFieldId);
|
|
1282
|
+
|
|
1283
|
+
assert(
|
|
1284
|
+
baseField !== undefined,
|
|
1285
|
+
0x9c2 /* Cross field key registered for empty field */,
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
const context = crossFieldTable.baseFieldToContext.get(baseField);
|
|
1289
|
+
if (context === undefined) {
|
|
1290
|
+
this.rebaseFieldWithoutNewChanges(
|
|
1291
|
+
baseField,
|
|
1292
|
+
baseFieldId,
|
|
1293
|
+
crossFieldTable,
|
|
1294
|
+
rebasedFields,
|
|
1295
|
+
rebasedNodes,
|
|
1296
|
+
genId,
|
|
1297
|
+
rebaseMetadata,
|
|
1298
|
+
);
|
|
1299
|
+
} else {
|
|
1300
|
+
this.rebaseInvalidatedField(
|
|
1301
|
+
baseField,
|
|
1302
|
+
crossFieldTable,
|
|
1303
|
+
context,
|
|
1304
|
+
rebaseMetadata,
|
|
1305
|
+
genId,
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1138
1309
|
}
|
|
1139
1310
|
}
|
|
1140
1311
|
|
|
1141
1312
|
private rebaseInvalidatedField(
|
|
1142
1313
|
baseField: FieldChange,
|
|
1143
1314
|
crossFieldTable: RebaseTable,
|
|
1315
|
+
context: RebaseFieldContext,
|
|
1144
1316
|
rebaseMetadata: RebaseRevisionMetadata,
|
|
1145
1317
|
genId: IdAllocator,
|
|
1146
|
-
allowInval = false,
|
|
1147
1318
|
): void {
|
|
1148
|
-
const context = crossFieldTable.baseFieldToContext.get(baseField);
|
|
1149
|
-
assert(context !== undefined, 0x852 /* Every field should have a context */);
|
|
1150
1319
|
const {
|
|
1151
1320
|
changeHandler,
|
|
1152
1321
|
change1: fieldChangeset,
|
|
@@ -1161,25 +1330,28 @@ export class ModularChangeFamily
|
|
|
1161
1330
|
return curr;
|
|
1162
1331
|
}
|
|
1163
1332
|
|
|
1164
|
-
if (base !== undefined) {
|
|
1165
|
-
|
|
1166
|
-
if (areEqualChangeAtomIds(base, id)) {
|
|
1167
|
-
return base;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1333
|
+
if (base !== undefined && getFromChangeAtomIdMap(context.baseNodeIds, base) === true) {
|
|
1334
|
+
return base;
|
|
1170
1335
|
}
|
|
1171
1336
|
|
|
1172
1337
|
return undefined;
|
|
1173
1338
|
};
|
|
1174
1339
|
|
|
1340
|
+
let allowInval = false;
|
|
1341
|
+
if (crossFieldTable.fieldsWithUnattachedChild.has(baseField)) {
|
|
1342
|
+
crossFieldTable.fieldsWithUnattachedChild.delete(baseField);
|
|
1343
|
+
allowInval = true;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1175
1346
|
context.rebasedChange.change = brand(
|
|
1176
1347
|
changeHandler.rebaser.rebase(
|
|
1177
1348
|
fieldChangeset,
|
|
1178
1349
|
baseChangeset,
|
|
1179
1350
|
rebaseChild,
|
|
1180
1351
|
genId,
|
|
1181
|
-
new
|
|
1352
|
+
new RebaseNodeManagerI(crossFieldTable, context.fieldId, allowInval),
|
|
1182
1353
|
rebaseMetadata,
|
|
1354
|
+
crossFieldTable.rebaseVersion,
|
|
1183
1355
|
),
|
|
1184
1356
|
);
|
|
1185
1357
|
}
|
|
@@ -1199,13 +1371,19 @@ export class ModularChangeFamily
|
|
|
1199
1371
|
}
|
|
1200
1372
|
const rebasedNode = getFromChangeAtomIdMap(rebasedNodes, nodeId);
|
|
1201
1373
|
if (rebasedNode !== undefined) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1374
|
+
const updatedRebasedNode: Mutable<NodeChangeset> = cloneNodeChangeset(rebasedNode);
|
|
1375
|
+
setInChangeAtomIdMap(rebasedNodes, nodeId, updatedRebasedNode);
|
|
1376
|
+
|
|
1377
|
+
if (updatedRebasedNode.fieldChanges === undefined) {
|
|
1378
|
+
updatedRebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
|
|
1204
1379
|
return;
|
|
1205
1380
|
}
|
|
1206
1381
|
|
|
1207
|
-
assert(
|
|
1208
|
-
|
|
1382
|
+
assert(
|
|
1383
|
+
!updatedRebasedNode.fieldChanges.has(fieldKey),
|
|
1384
|
+
0x9c4 /* Expected an empty field */,
|
|
1385
|
+
);
|
|
1386
|
+
updatedRebasedNode.fieldChanges.set(fieldKey, rebasedField);
|
|
1209
1387
|
return;
|
|
1210
1388
|
}
|
|
1211
1389
|
|
|
@@ -1216,14 +1394,14 @@ export class ModularChangeFamily
|
|
|
1216
1394
|
setInChangeAtomIdMap(rebasedNodes, nodeId, newNode);
|
|
1217
1395
|
setInChangeAtomIdMap(table.baseToRebasedNodeId, nodeId, nodeId);
|
|
1218
1396
|
|
|
1219
|
-
const
|
|
1397
|
+
const parentBase = getNodeParent(table.baseChange, nodeId);
|
|
1220
1398
|
|
|
1221
1399
|
this.attachRebasedNode(
|
|
1222
1400
|
rebasedFields,
|
|
1223
1401
|
rebasedNodes,
|
|
1224
1402
|
table,
|
|
1225
1403
|
nodeId,
|
|
1226
|
-
|
|
1404
|
+
parentBase,
|
|
1227
1405
|
idAllocator,
|
|
1228
1406
|
metadata,
|
|
1229
1407
|
);
|
|
@@ -1234,65 +1412,100 @@ export class ModularChangeFamily
|
|
|
1234
1412
|
rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1235
1413
|
table: RebaseTable,
|
|
1236
1414
|
baseNodeId: NodeId,
|
|
1237
|
-
|
|
1415
|
+
parentBase: NodeLocation,
|
|
1238
1416
|
idAllocator: IdAllocator,
|
|
1239
1417
|
metadata: RebaseRevisionMetadata,
|
|
1240
1418
|
): void {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1419
|
+
if (parentBase.root !== undefined) {
|
|
1420
|
+
const renamedRoot = firstAttachIdFromDetachId(
|
|
1421
|
+
table.baseChange.rootNodes,
|
|
1422
|
+
parentBase.root,
|
|
1423
|
+
1,
|
|
1424
|
+
).value;
|
|
1425
|
+
|
|
1426
|
+
const attachField = table.baseChange.crossFieldKeys.getFirst(
|
|
1427
|
+
{ ...renamedRoot, target: CrossFieldTarget.Destination },
|
|
1428
|
+
1,
|
|
1429
|
+
).value;
|
|
1430
|
+
|
|
1431
|
+
if (attachField === undefined) {
|
|
1432
|
+
const baseDetachLocation = table.baseChange.rootNodes.detachLocations.getFirst(
|
|
1433
|
+
parentBase.root,
|
|
1434
|
+
1,
|
|
1435
|
+
).value;
|
|
1436
|
+
|
|
1437
|
+
assignRootChange(
|
|
1438
|
+
table.rebasedRootNodes,
|
|
1439
|
+
table.rebasedNodeToParent,
|
|
1440
|
+
renamedRoot,
|
|
1441
|
+
baseNodeId,
|
|
1442
|
+
baseDetachLocation,
|
|
1443
|
+
table.rebaseVersion,
|
|
1444
|
+
);
|
|
1445
|
+
|
|
1446
|
+
// We need to make sure the rebased changeset includes the detach location,
|
|
1447
|
+
// so we add that field to `affectedBaseFields` unless it's already been processed.
|
|
1448
|
+
if (
|
|
1449
|
+
baseDetachLocation !== undefined &&
|
|
1450
|
+
!table.baseFieldToContext.has(
|
|
1451
|
+
fieldChangeFromId(table.baseChange, baseDetachLocation),
|
|
1452
|
+
)
|
|
1453
|
+
) {
|
|
1454
|
+
table.affectedBaseFields.set(fieldIdKeyFromFieldId(baseDetachLocation), true);
|
|
1455
|
+
}
|
|
1456
|
+
} else {
|
|
1457
|
+
// The base change inserts this node into `attachField`, so the rebased change should represent this node there.
|
|
1458
|
+
const normalizedAttachField = normalizeFieldId(
|
|
1459
|
+
attachField,
|
|
1460
|
+
table.baseChange.nodeAliases,
|
|
1461
|
+
);
|
|
1462
|
+
|
|
1463
|
+
const entry: DetachedNodeEntry = table.entries.getFirst(renamedRoot, 1).value ?? {};
|
|
1464
|
+
table.entries.set(renamedRoot, 1, { ...entry, nodeChange: baseNodeId });
|
|
1465
|
+
table.affectedBaseFields.set(fieldIdKeyFromFieldId(normalizedAttachField), true);
|
|
1466
|
+
this.attachRebasedNode(
|
|
1467
|
+
rebasedFields,
|
|
1468
|
+
rebasedNodes,
|
|
1469
|
+
table,
|
|
1470
|
+
baseNodeId,
|
|
1471
|
+
{ field: normalizedAttachField },
|
|
1472
|
+
idAllocator,
|
|
1473
|
+
metadata,
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const parentFieldIdBase = parentBase.field;
|
|
1481
|
+
const baseFieldChange = fieldChangeFromId(table.baseChange, parentFieldIdBase);
|
|
1246
1482
|
|
|
1247
1483
|
const rebasedFieldId = rebasedFieldIdFromBaseId(table, parentFieldIdBase);
|
|
1248
|
-
setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, rebasedFieldId);
|
|
1484
|
+
setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, { field: rebasedFieldId });
|
|
1249
1485
|
|
|
1250
1486
|
const context = table.baseFieldToContext.get(baseFieldChange);
|
|
1251
1487
|
if (context !== undefined) {
|
|
1252
1488
|
// We've already processed this field.
|
|
1253
|
-
// The new child node will be attached in
|
|
1254
|
-
|
|
1255
|
-
|
|
1489
|
+
// The new child node will be attached in the next pass.
|
|
1490
|
+
// Note that adding to `fieldsWithUnattachedChild` allows that field to generate new invalidations,
|
|
1491
|
+
// so to avoid invalidation cycles we make sure we only add to it once per new unattached child.
|
|
1492
|
+
// This is done by checking whether `context.baseNodeIds` already contained `baseNodeId`.
|
|
1493
|
+
if (setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true)) {
|
|
1494
|
+
table.fieldsWithUnattachedChild.add(baseFieldChange);
|
|
1495
|
+
table.affectedBaseFields.set(fieldIdKeyFromFieldId(parentFieldIdBase), true);
|
|
1496
|
+
}
|
|
1256
1497
|
return;
|
|
1257
1498
|
}
|
|
1258
1499
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
change: brand(handler.createEmpty()),
|
|
1264
|
-
};
|
|
1265
|
-
|
|
1266
|
-
const rebasedChangeset = handler.rebaser.rebase(
|
|
1267
|
-
handler.createEmpty(),
|
|
1268
|
-
baseFieldChange.change,
|
|
1269
|
-
(_idNew, idBase) =>
|
|
1270
|
-
idBase !== undefined && areEqualChangeAtomIds(idBase, baseNodeId)
|
|
1271
|
-
? baseNodeId
|
|
1272
|
-
: undefined,
|
|
1273
|
-
idAllocator,
|
|
1274
|
-
new RebaseManager(table, baseFieldChange, rebasedFieldId),
|
|
1275
|
-
metadata,
|
|
1276
|
-
);
|
|
1277
|
-
|
|
1278
|
-
const rebasedField: FieldChange = { ...baseFieldChange, change: brand(rebasedChangeset) };
|
|
1279
|
-
table.rebasedFields.add(rebasedField);
|
|
1280
|
-
table.baseFieldToContext.set(baseFieldChange, {
|
|
1281
|
-
newChange: fieldChange,
|
|
1282
|
-
baseChange: baseFieldChange,
|
|
1283
|
-
rebasedChange: rebasedField,
|
|
1284
|
-
fieldId: rebasedFieldId,
|
|
1285
|
-
baseNodeIds: [],
|
|
1286
|
-
});
|
|
1287
|
-
|
|
1288
|
-
this.attachRebasedField(
|
|
1500
|
+
this.rebaseFieldWithoutNewChanges(
|
|
1501
|
+
baseFieldChange,
|
|
1502
|
+
parentFieldIdBase,
|
|
1503
|
+
table,
|
|
1289
1504
|
rebasedFields,
|
|
1290
1505
|
rebasedNodes,
|
|
1291
|
-
table,
|
|
1292
|
-
rebasedField,
|
|
1293
|
-
rebasedFieldId,
|
|
1294
1506
|
idAllocator,
|
|
1295
1507
|
metadata,
|
|
1508
|
+
baseNodeId,
|
|
1296
1509
|
);
|
|
1297
1510
|
}
|
|
1298
1511
|
|
|
@@ -1331,7 +1544,7 @@ export class ModularChangeFamily
|
|
|
1331
1544
|
change2: baseChangeset,
|
|
1332
1545
|
} = this.normalizeFieldChanges(fieldChange, baseChange);
|
|
1333
1546
|
|
|
1334
|
-
const manager = new
|
|
1547
|
+
const manager = new RebaseNodeManagerI(crossFieldTable, fieldId);
|
|
1335
1548
|
|
|
1336
1549
|
const rebasedField = changeHandler.rebaser.rebase(
|
|
1337
1550
|
fieldChangeset,
|
|
@@ -1340,6 +1553,7 @@ export class ModularChangeFamily
|
|
|
1340
1553
|
genId,
|
|
1341
1554
|
manager,
|
|
1342
1555
|
revisionMetadata,
|
|
1556
|
+
crossFieldTable.rebaseVersion,
|
|
1343
1557
|
);
|
|
1344
1558
|
|
|
1345
1559
|
const rebasedFieldChange: FieldChange = {
|
|
@@ -1354,7 +1568,7 @@ export class ModularChangeFamily
|
|
|
1354
1568
|
newChange: fieldChange,
|
|
1355
1569
|
rebasedChange: rebasedFieldChange,
|
|
1356
1570
|
fieldId,
|
|
1357
|
-
baseNodeIds:
|
|
1571
|
+
baseNodeIds: newTupleBTree(),
|
|
1358
1572
|
});
|
|
1359
1573
|
|
|
1360
1574
|
crossFieldTable.rebasedFields.add(rebasedFieldChange);
|
|
@@ -1370,8 +1584,16 @@ export class ModularChangeFamily
|
|
|
1370
1584
|
crossFieldTable: RebaseTable,
|
|
1371
1585
|
revisionMetadata: RebaseRevisionMetadata,
|
|
1372
1586
|
): NodeChangeset {
|
|
1373
|
-
const change = nodeChangeFromId(
|
|
1374
|
-
|
|
1587
|
+
const change = nodeChangeFromId(
|
|
1588
|
+
crossFieldTable.newChange.nodeChanges,
|
|
1589
|
+
crossFieldTable.newChange.nodeAliases,
|
|
1590
|
+
newId,
|
|
1591
|
+
);
|
|
1592
|
+
const over = nodeChangeFromId(
|
|
1593
|
+
crossFieldTable.baseChange.nodeChanges,
|
|
1594
|
+
crossFieldTable.baseChange.nodeAliases,
|
|
1595
|
+
baseId,
|
|
1596
|
+
);
|
|
1375
1597
|
|
|
1376
1598
|
const baseMap: FieldChangeMap = over?.fieldChanges ?? new Map();
|
|
1377
1599
|
|
|
@@ -1387,7 +1609,7 @@ export class ModularChangeFamily
|
|
|
1387
1609
|
)
|
|
1388
1610
|
: change.fieldChanges;
|
|
1389
1611
|
|
|
1390
|
-
const rebasedChange: NodeChangeset = {};
|
|
1612
|
+
const rebasedChange: Mutable<NodeChangeset> = {};
|
|
1391
1613
|
|
|
1392
1614
|
if (fieldChanges !== undefined && fieldChanges.size > 0) {
|
|
1393
1615
|
rebasedChange.fieldChanges = fieldChanges;
|
|
@@ -1405,6 +1627,37 @@ export class ModularChangeFamily
|
|
|
1405
1627
|
return rebasedChange;
|
|
1406
1628
|
}
|
|
1407
1629
|
|
|
1630
|
+
private updateConstraints(
|
|
1631
|
+
rebasedFields: FieldChangeMap,
|
|
1632
|
+
rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1633
|
+
rebasedRoots: RootNodeTable,
|
|
1634
|
+
constraintState: ConstraintState,
|
|
1635
|
+
revertConstraintState: ConstraintState,
|
|
1636
|
+
): void {
|
|
1637
|
+
this.updateConstraintsForFields(
|
|
1638
|
+
rebasedFields,
|
|
1639
|
+
NodeAttachState.Attached,
|
|
1640
|
+
NodeAttachState.Attached,
|
|
1641
|
+
constraintState,
|
|
1642
|
+
revertConstraintState,
|
|
1643
|
+
rebasedNodes,
|
|
1644
|
+
);
|
|
1645
|
+
|
|
1646
|
+
for (const [_detachId, nodeId] of rebasedRoots.nodeChanges.entries()) {
|
|
1647
|
+
// XXX: This is incorrect if the rebased changeset attaches the node.
|
|
1648
|
+
// Efficiently computing whether the changeset attaches the node would require maintaining a mapping from node ID to attach ID.
|
|
1649
|
+
const detachedInOutput = true;
|
|
1650
|
+
this.updateConstraintsForNode(
|
|
1651
|
+
nodeId,
|
|
1652
|
+
NodeAttachState.Detached,
|
|
1653
|
+
detachedInOutput ? NodeAttachState.Detached : NodeAttachState.Attached,
|
|
1654
|
+
rebasedNodes,
|
|
1655
|
+
constraintState,
|
|
1656
|
+
revertConstraintState,
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1408
1661
|
private updateConstraintsForFields(
|
|
1409
1662
|
fields: FieldChangeMap,
|
|
1410
1663
|
parentInputAttachState: NodeAttachState,
|
|
@@ -1415,20 +1668,18 @@ export class ModularChangeFamily
|
|
|
1415
1668
|
): void {
|
|
1416
1669
|
for (const field of fields.values()) {
|
|
1417
1670
|
const handler = getChangeHandler(this.fieldKinds, field.fieldKind);
|
|
1418
|
-
for (const [nodeId
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
? NodeAttachState.Detached
|
|
1423
|
-
: NodeAttachState.Attached;
|
|
1424
|
-
const isOutputDetached = outputIndex === undefined;
|
|
1671
|
+
for (const [nodeId] of handler.getNestedChanges(field.change)) {
|
|
1672
|
+
// XXX: This is incorrect if the rebased changeset detaches this node.
|
|
1673
|
+
// Efficiently computing whether the changeset detaches the node would require maintaining a mapping from node ID to detach ID.
|
|
1674
|
+
const isOutputDetached = false;
|
|
1425
1675
|
const outputAttachState =
|
|
1426
1676
|
parentOutputAttachState === NodeAttachState.Detached || isOutputDetached
|
|
1427
1677
|
? NodeAttachState.Detached
|
|
1428
1678
|
: NodeAttachState.Attached;
|
|
1679
|
+
|
|
1429
1680
|
this.updateConstraintsForNode(
|
|
1430
1681
|
nodeId,
|
|
1431
|
-
|
|
1682
|
+
parentInputAttachState,
|
|
1432
1683
|
outputAttachState,
|
|
1433
1684
|
nodes,
|
|
1434
1685
|
constraintState,
|
|
@@ -1446,12 +1697,15 @@ export class ModularChangeFamily
|
|
|
1446
1697
|
constraintState: ConstraintState,
|
|
1447
1698
|
revertConstraintState: ConstraintState,
|
|
1448
1699
|
): void {
|
|
1449
|
-
const node =
|
|
1450
|
-
|
|
1700
|
+
const node = getFromChangeAtomIdMap(nodes, nodeId) ?? fail(0xb24 /* Unknown node ID */);
|
|
1701
|
+
|
|
1702
|
+
const updatedNode: Mutable<NodeChangeset> = { ...node };
|
|
1703
|
+
setInChangeAtomIdMap(nodes, nodeId, updatedNode);
|
|
1704
|
+
|
|
1451
1705
|
if (node.nodeExistsConstraint !== undefined) {
|
|
1452
1706
|
const isNowViolated = inputAttachState === NodeAttachState.Detached;
|
|
1453
1707
|
if (node.nodeExistsConstraint.violated !== isNowViolated) {
|
|
1454
|
-
|
|
1708
|
+
updatedNode.nodeExistsConstraint = {
|
|
1455
1709
|
...node.nodeExistsConstraint,
|
|
1456
1710
|
violated: isNowViolated,
|
|
1457
1711
|
};
|
|
@@ -1461,7 +1715,7 @@ export class ModularChangeFamily
|
|
|
1461
1715
|
if (node.nodeExistsConstraintOnRevert !== undefined) {
|
|
1462
1716
|
const isNowViolated = outputAttachState === NodeAttachState.Detached;
|
|
1463
1717
|
if (node.nodeExistsConstraintOnRevert.violated !== isNowViolated) {
|
|
1464
|
-
|
|
1718
|
+
updatedNode.nodeExistsConstraintOnRevert = {
|
|
1465
1719
|
...node.nodeExistsConstraintOnRevert,
|
|
1466
1720
|
violated: isNowViolated,
|
|
1467
1721
|
};
|
|
@@ -1483,7 +1737,13 @@ export class ModularChangeFamily
|
|
|
1483
1737
|
|
|
1484
1738
|
private pruneFieldMap(
|
|
1485
1739
|
changeset: FieldChangeMap | undefined,
|
|
1740
|
+
parentId: NodeId | undefined,
|
|
1486
1741
|
nodeMap: ChangeAtomIdBTree<NodeChangeset>,
|
|
1742
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
1743
|
+
aliases: ChangeAtomIdBTree<NodeId>,
|
|
1744
|
+
roots: RootNodeTable,
|
|
1745
|
+
fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
|
|
1746
|
+
fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
|
|
1487
1747
|
): FieldChangeMap | undefined {
|
|
1488
1748
|
if (changeset === undefined) {
|
|
1489
1749
|
return undefined;
|
|
@@ -1494,10 +1754,48 @@ export class ModularChangeFamily
|
|
|
1494
1754
|
const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
|
|
1495
1755
|
|
|
1496
1756
|
const prunedFieldChangeset = handler.rebaser.prune(fieldChange.change, (nodeId) =>
|
|
1497
|
-
this.pruneNodeChange(
|
|
1757
|
+
this.pruneNodeChange(
|
|
1758
|
+
nodeId,
|
|
1759
|
+
nodeMap,
|
|
1760
|
+
nodeToParent,
|
|
1761
|
+
aliases,
|
|
1762
|
+
roots,
|
|
1763
|
+
fieldsWithRootMoves,
|
|
1764
|
+
fieldsToRootChanges,
|
|
1765
|
+
),
|
|
1498
1766
|
);
|
|
1499
1767
|
|
|
1500
|
-
|
|
1768
|
+
const fieldId: FieldId = { nodeId: parentId, field };
|
|
1769
|
+
const fieldIdKey = fieldIdKeyFromFieldId(fieldId);
|
|
1770
|
+
const rootsWithChanges = fieldsToRootChanges.get(fieldIdKey) ?? [];
|
|
1771
|
+
let hasRootWithNodeChange = false;
|
|
1772
|
+
for (const rootId of rootsWithChanges) {
|
|
1773
|
+
const nodeId =
|
|
1774
|
+
getFromChangeAtomIdMap(roots.nodeChanges, rootId) ?? fail("No root change found");
|
|
1775
|
+
|
|
1776
|
+
const isRootChangeEmpty =
|
|
1777
|
+
this.pruneNodeChange(
|
|
1778
|
+
nodeId,
|
|
1779
|
+
nodeMap,
|
|
1780
|
+
nodeToParent,
|
|
1781
|
+
aliases,
|
|
1782
|
+
roots,
|
|
1783
|
+
fieldsWithRootMoves,
|
|
1784
|
+
fieldsToRootChanges,
|
|
1785
|
+
) === undefined;
|
|
1786
|
+
|
|
1787
|
+
if (isRootChangeEmpty) {
|
|
1788
|
+
roots.nodeChanges.delete([rootId.revision, rootId.localId]);
|
|
1789
|
+
tryRemoveDetachLocation(roots, rootId, 1);
|
|
1790
|
+
} else {
|
|
1791
|
+
hasRootWithNodeChange = true;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
const hasRootChanges =
|
|
1796
|
+
hasRootWithNodeChange || fieldsWithRootMoves.get(fieldIdKey) === true;
|
|
1797
|
+
|
|
1798
|
+
if (!handler.isEmpty(prunedFieldChangeset) || hasRootChanges) {
|
|
1501
1799
|
prunedChangeset.set(field, { ...fieldChange, change: brand(prunedFieldChangeset) });
|
|
1502
1800
|
}
|
|
1503
1801
|
}
|
|
@@ -1505,15 +1803,65 @@ export class ModularChangeFamily
|
|
|
1505
1803
|
return prunedChangeset.size > 0 ? prunedChangeset : undefined;
|
|
1506
1804
|
}
|
|
1507
1805
|
|
|
1806
|
+
private pruneRoots(
|
|
1807
|
+
roots: RootNodeTable,
|
|
1808
|
+
nodeMap: ChangeAtomIdBTree<NodeChangeset>,
|
|
1809
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
1810
|
+
aliases: ChangeAtomIdBTree<NodeId>,
|
|
1811
|
+
fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
|
|
1812
|
+
fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
|
|
1813
|
+
): RootNodeTable {
|
|
1814
|
+
const pruned: RootNodeTable = { ...roots, nodeChanges: newTupleBTree() };
|
|
1815
|
+
for (const [rootIdKey, nodeId] of roots.nodeChanges.entries()) {
|
|
1816
|
+
const rootId: ChangeAtomId = { revision: rootIdKey[0], localId: rootIdKey[1] };
|
|
1817
|
+
const hasDetachLocation = roots.detachLocations.getFirst(rootId, 1).value !== undefined;
|
|
1818
|
+
|
|
1819
|
+
// If the root has a detach location it should be pruned by recursion when pruning the field it was detached from.
|
|
1820
|
+
const prunedId = hasDetachLocation
|
|
1821
|
+
? nodeId
|
|
1822
|
+
: this.pruneNodeChange(
|
|
1823
|
+
nodeId,
|
|
1824
|
+
nodeMap,
|
|
1825
|
+
nodeToParent,
|
|
1826
|
+
aliases,
|
|
1827
|
+
roots,
|
|
1828
|
+
fieldsWithRootMoves,
|
|
1829
|
+
fieldsToRootChanges,
|
|
1830
|
+
);
|
|
1831
|
+
|
|
1832
|
+
if (prunedId !== undefined) {
|
|
1833
|
+
pruned.nodeChanges.set(rootIdKey, prunedId);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
tryRemoveDetachLocation(pruned, rootId, 1);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return pruned;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1508
1842
|
private pruneNodeChange(
|
|
1509
1843
|
nodeId: NodeId,
|
|
1510
|
-
|
|
1844
|
+
nodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1845
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
1846
|
+
aliases: ChangeAtomIdBTree<NodeId>,
|
|
1847
|
+
roots: RootNodeTable,
|
|
1848
|
+
fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
|
|
1849
|
+
fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
|
|
1511
1850
|
): NodeId | undefined {
|
|
1512
|
-
const changeset = nodeChangeFromId(
|
|
1851
|
+
const changeset = nodeChangeFromId(nodes, aliases, nodeId);
|
|
1513
1852
|
const prunedFields =
|
|
1514
1853
|
changeset.fieldChanges === undefined
|
|
1515
1854
|
? undefined
|
|
1516
|
-
: this.pruneFieldMap(
|
|
1855
|
+
: this.pruneFieldMap(
|
|
1856
|
+
changeset.fieldChanges,
|
|
1857
|
+
nodeId,
|
|
1858
|
+
nodes,
|
|
1859
|
+
nodeToParent,
|
|
1860
|
+
aliases,
|
|
1861
|
+
roots,
|
|
1862
|
+
fieldsWithRootMoves,
|
|
1863
|
+
fieldsToRootChanges,
|
|
1864
|
+
);
|
|
1517
1865
|
|
|
1518
1866
|
const prunedChange = { ...changeset, fieldChanges: prunedFields };
|
|
1519
1867
|
if (prunedChange.fieldChanges === undefined) {
|
|
@@ -1521,17 +1869,27 @@ export class ModularChangeFamily
|
|
|
1521
1869
|
}
|
|
1522
1870
|
|
|
1523
1871
|
if (isEmptyNodeChangeset(prunedChange)) {
|
|
1524
|
-
|
|
1872
|
+
const nodeIdKey: [RevisionTag | undefined, ChangesetLocalId] = [
|
|
1873
|
+
nodeId.revision,
|
|
1874
|
+
nodeId.localId,
|
|
1875
|
+
];
|
|
1876
|
+
|
|
1877
|
+
// TODO: Shouldn't we also delete all aliases associated with this node?
|
|
1878
|
+
nodes.delete(nodeIdKey);
|
|
1879
|
+
nodeToParent.delete(nodeIdKey);
|
|
1525
1880
|
return undefined;
|
|
1526
1881
|
} else {
|
|
1527
|
-
setInChangeAtomIdMap(
|
|
1882
|
+
setInChangeAtomIdMap(nodes, nodeId, prunedChange);
|
|
1528
1883
|
return nodeId;
|
|
1529
1884
|
}
|
|
1530
1885
|
}
|
|
1531
1886
|
|
|
1532
1887
|
public getRevisions(change: ModularChangeset): Set<RevisionTag | undefined> {
|
|
1888
|
+
if (change.revisions === undefined || change.revisions.length === 0) {
|
|
1889
|
+
return new Set([undefined]);
|
|
1890
|
+
}
|
|
1533
1891
|
const aggregated: Set<RevisionTag | undefined> = new Set();
|
|
1534
|
-
for (const revInfo of change.revisions
|
|
1892
|
+
for (const revInfo of change.revisions) {
|
|
1535
1893
|
aggregated.add(revInfo.revision);
|
|
1536
1894
|
}
|
|
1537
1895
|
return aggregated;
|
|
@@ -1548,8 +1906,11 @@ export class ModularChangeFamily
|
|
|
1548
1906
|
const updatedNodeToParent = replaceIdMapRevisions(
|
|
1549
1907
|
change.nodeToParent,
|
|
1550
1908
|
replacer,
|
|
1551
|
-
(
|
|
1552
|
-
|
|
1909
|
+
(location) =>
|
|
1910
|
+
replaceNodeLocationRevision(
|
|
1911
|
+
normalizeNodeLocation(location, change.nodeAliases),
|
|
1912
|
+
replacer,
|
|
1913
|
+
),
|
|
1553
1914
|
);
|
|
1554
1915
|
|
|
1555
1916
|
const updated: Mutable<ModularChangeset> = {
|
|
@@ -1557,6 +1918,7 @@ export class ModularChangeFamily
|
|
|
1557
1918
|
fieldChanges: updatedFields,
|
|
1558
1919
|
nodeChanges: updatedNodes,
|
|
1559
1920
|
nodeToParent: updatedNodeToParent,
|
|
1921
|
+
rootNodes: replaceRootTableRevision(change.rootNodes, replacer, change.nodeAliases),
|
|
1560
1922
|
|
|
1561
1923
|
// We've updated all references to old node IDs, so we no longer need an alias table.
|
|
1562
1924
|
nodeAliases: newTupleBTree(),
|
|
@@ -1620,7 +1982,7 @@ export class ModularChangeFamily
|
|
|
1620
1982
|
fields: FieldChangeMap,
|
|
1621
1983
|
nodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
1622
1984
|
): CrossFieldKeyTable {
|
|
1623
|
-
const keys: CrossFieldKeyTable =
|
|
1985
|
+
const keys: CrossFieldKeyTable = newCrossFieldRangeTable();
|
|
1624
1986
|
this.populateCrossFieldKeyTableForFieldMap(keys, fields, undefined);
|
|
1625
1987
|
nodes.forEachPair(([revision, localId], node) => {
|
|
1626
1988
|
if (node.fieldChanges !== undefined) {
|
|
@@ -1661,63 +2023,6 @@ export class ModularChangeFamily
|
|
|
1661
2023
|
return { fieldKind, change: brand(emptyChange) };
|
|
1662
2024
|
}
|
|
1663
2025
|
|
|
1664
|
-
public validateChangeset(change: ModularChangeset): void {
|
|
1665
|
-
let numNodes = this.validateFieldChanges(change, change.fieldChanges, undefined);
|
|
1666
|
-
|
|
1667
|
-
for (const [[revision, localId], node] of change.nodeChanges.entries()) {
|
|
1668
|
-
if (node.fieldChanges === undefined) {
|
|
1669
|
-
continue;
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
const nodeId: NodeId = { revision, localId };
|
|
1673
|
-
const numChildren = this.validateFieldChanges(change, node.fieldChanges, nodeId);
|
|
1674
|
-
|
|
1675
|
-
numNodes += numChildren;
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
assert(
|
|
1679
|
-
numNodes === change.nodeChanges.size,
|
|
1680
|
-
0xa4d /* Node table contains unparented nodes */,
|
|
1681
|
-
);
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
/**
|
|
1685
|
-
* Asserts that each child and cross field key in each field has a correct entry in
|
|
1686
|
-
* `nodeToParent` or `crossFieldKeyTable`.
|
|
1687
|
-
* @returns the number of children found.
|
|
1688
|
-
*/
|
|
1689
|
-
private validateFieldChanges(
|
|
1690
|
-
change: ModularChangeset,
|
|
1691
|
-
fieldChanges: FieldChangeMap,
|
|
1692
|
-
nodeParent: NodeId | undefined,
|
|
1693
|
-
): number {
|
|
1694
|
-
let numChildren = 0;
|
|
1695
|
-
for (const [field, fieldChange] of fieldChanges.entries()) {
|
|
1696
|
-
const fieldId = { nodeId: nodeParent, field };
|
|
1697
|
-
const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
|
|
1698
|
-
for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
|
|
1699
|
-
const parentFieldId = getParentFieldId(change, child);
|
|
1700
|
-
assert(
|
|
1701
|
-
areEqualFieldIds(parentFieldId, fieldId),
|
|
1702
|
-
0xa4e /* Inconsistent node parentage */,
|
|
1703
|
-
);
|
|
1704
|
-
numChildren += 1;
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
|
|
1708
|
-
const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
|
|
1709
|
-
assert(
|
|
1710
|
-
fields.length === 1 &&
|
|
1711
|
-
fields[0] !== undefined &&
|
|
1712
|
-
areEqualFieldIds(fields[0], fieldId),
|
|
1713
|
-
0xa4f /* Inconsistent cross field keys */,
|
|
1714
|
-
);
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
return numChildren;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
2026
|
private getEffectiveChange(change: ModularChangeset): ModularChangeset {
|
|
1722
2027
|
if (hasConflicts(change)) {
|
|
1723
2028
|
return this.muteChange(change);
|
|
@@ -1731,7 +2036,8 @@ export class ModularChangeFamily
|
|
|
1731
2036
|
private muteChange(change: ModularChangeset): ModularChangeset {
|
|
1732
2037
|
const muted: Mutable<ModularChangeset> = {
|
|
1733
2038
|
...change,
|
|
1734
|
-
|
|
2039
|
+
rootNodes: muteRootChanges(change.rootNodes),
|
|
2040
|
+
crossFieldKeys: newCrossFieldRangeTable(),
|
|
1735
2041
|
fieldChanges: this.muteFieldChanges(change.fieldChanges),
|
|
1736
2042
|
nodeChanges: brand(change.nodeChanges.mapValues((v) => this.muteNodeChange(v))),
|
|
1737
2043
|
};
|
|
@@ -1768,7 +2074,7 @@ function replaceCrossFieldKeyTableRevisions(
|
|
|
1768
2074
|
replacer: RevisionReplacer,
|
|
1769
2075
|
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
1770
2076
|
): CrossFieldKeyTable {
|
|
1771
|
-
const updated: CrossFieldKeyTable =
|
|
2077
|
+
const updated: CrossFieldKeyTable = newCrossFieldRangeTable();
|
|
1772
2078
|
for (const entry of table.entries()) {
|
|
1773
2079
|
const key = entry.start;
|
|
1774
2080
|
const updatedKey: CrossFieldKey = replacer.getUpdatedAtomId(key);
|
|
@@ -1875,6 +2181,20 @@ function composeBuildsDestroysAndRefreshers(
|
|
|
1875
2181
|
}
|
|
1876
2182
|
}
|
|
1877
2183
|
|
|
2184
|
+
// It's possible to have a build and a refresher for the same root because an attach operation need not be performed in the same changeset as the corresponding build.
|
|
2185
|
+
if (change1.builds !== undefined && change2.refreshers !== undefined) {
|
|
2186
|
+
for (const [key, chunk] of change2.refreshers.entries()) {
|
|
2187
|
+
assert(chunk.topLevelLength === 1, "Expected refresher chunk to have length 1");
|
|
2188
|
+
const match = change1.builds.getPairOrNextLower(key);
|
|
2189
|
+
if (match !== undefined) {
|
|
2190
|
+
const [buildKey, buildChunk] = match;
|
|
2191
|
+
if (buildKey[0] === key[0] && buildKey[1] + buildChunk.topLevelLength > key[1]) {
|
|
2192
|
+
allRefreshers.delete(key);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
|
|
1878
2198
|
return { allBuilds, allDestroys, allRefreshers };
|
|
1879
2199
|
}
|
|
1880
2200
|
|
|
@@ -1906,27 +2226,77 @@ export function* relevantRemovedRoots(
|
|
|
1906
2226
|
change: ModularChangeset,
|
|
1907
2227
|
fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
|
|
1908
2228
|
): Iterable<DeltaDetachedNodeId> {
|
|
1909
|
-
|
|
2229
|
+
const rootIds: ChangeAtomIdRangeMap<boolean> = newChangeAtomIdRangeMap();
|
|
2230
|
+
addAttachesToSet(change, rootIds);
|
|
2231
|
+
addRenamesToSet(change, rootIds);
|
|
2232
|
+
|
|
2233
|
+
for (const [[revision, localId]] of change.rootNodes.nodeChanges.entries()) {
|
|
2234
|
+
rootIds.set({ revision, localId }, 1, true);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
for (const entry of rootIds.entries()) {
|
|
2238
|
+
for (let offset = 0; offset < entry.length; offset++) {
|
|
2239
|
+
const detachId = offsetChangeAtomId(entry.start, offset);
|
|
2240
|
+
yield makeDetachedNodeId(detachId.revision, detachId.localId);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
1910
2243
|
}
|
|
1911
2244
|
|
|
1912
|
-
function*
|
|
1913
|
-
change
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2245
|
+
export function* getBuildIds(change: ModularChangeset): Iterable<DeltaDetachedNodeId> {
|
|
2246
|
+
if (change.builds !== undefined) {
|
|
2247
|
+
for (const [[revision, localId]] of change.builds.entries()) {
|
|
2248
|
+
yield makeDetachedNodeId(revision, localId);
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
function addAttachesToSet(
|
|
2254
|
+
change: ModularChangeset,
|
|
2255
|
+
rootIds: ChangeAtomIdRangeMap<boolean>,
|
|
2256
|
+
): void {
|
|
2257
|
+
// This includes each attach which does not have a corresponding detach.
|
|
2258
|
+
for (const entry of change.crossFieldKeys.entries()) {
|
|
2259
|
+
if (entry.start.target !== CrossFieldTarget.Destination) {
|
|
2260
|
+
continue;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
for (const detachIdEntry of change.rootNodes.newToOldId.getAll2(
|
|
2264
|
+
entry.start,
|
|
2265
|
+
entry.length,
|
|
2266
|
+
)) {
|
|
2267
|
+
const detachId =
|
|
2268
|
+
detachIdEntry.value ?? offsetChangeAtomId(entry.start, detachIdEntry.offset);
|
|
2269
|
+
for (const detachEntry of change.crossFieldKeys.getAll2(
|
|
2270
|
+
{ ...detachId, target: CrossFieldTarget.Source },
|
|
2271
|
+
detachIdEntry.length,
|
|
2272
|
+
)) {
|
|
2273
|
+
if (detachEntry.value === undefined) {
|
|
2274
|
+
rootIds.set(
|
|
2275
|
+
offsetChangeAtomId(detachId, detachEntry.offset),
|
|
2276
|
+
detachEntry.length,
|
|
2277
|
+
true,
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
1927
2280
|
}
|
|
1928
|
-
}
|
|
1929
|
-
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
function addRenamesToSet(
|
|
2286
|
+
change: ModularChangeset,
|
|
2287
|
+
rootIds: ChangeAtomIdRangeMap<boolean>,
|
|
2288
|
+
): void {
|
|
2289
|
+
for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
|
|
2290
|
+
for (const detachEntry of change.crossFieldKeys.getAll2(
|
|
2291
|
+
{ ...renameEntry.start, target: CrossFieldTarget.Source },
|
|
2292
|
+
renameEntry.length,
|
|
2293
|
+
)) {
|
|
2294
|
+
// We only want to include renames of nodes which are detached in the input context of the changeset.
|
|
2295
|
+
// So if there is a detach for the node, the rename is not relevant.
|
|
2296
|
+
if (detachEntry.value === undefined) {
|
|
2297
|
+
rootIds.set(renameEntry.start, renameEntry.length, true);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
1930
2300
|
}
|
|
1931
2301
|
}
|
|
1932
2302
|
|
|
@@ -1995,6 +2365,7 @@ export function updateRefreshers(
|
|
|
1995
2365
|
constraintViolationCountOnRevert,
|
|
1996
2366
|
builds,
|
|
1997
2367
|
destroys,
|
|
2368
|
+
rootNodes,
|
|
1998
2369
|
} = change;
|
|
1999
2370
|
|
|
2000
2371
|
return makeModularChangeset({
|
|
@@ -2003,6 +2374,7 @@ export function updateRefreshers(
|
|
|
2003
2374
|
nodeToParent,
|
|
2004
2375
|
nodeAliases,
|
|
2005
2376
|
crossFieldKeys,
|
|
2377
|
+
rootNodes,
|
|
2006
2378
|
maxId: maxId as number,
|
|
2007
2379
|
revisions,
|
|
2008
2380
|
constraintViolationCount,
|
|
@@ -2025,18 +2397,42 @@ export function intoDelta(
|
|
|
2025
2397
|
): DeltaRoot {
|
|
2026
2398
|
const change = taggedChange.change;
|
|
2027
2399
|
const rootDelta: Mutable<DeltaRoot> = {};
|
|
2028
|
-
const global: DeltaDetachedNodeChanges[] = [];
|
|
2029
|
-
const rename: DeltaDetachedNodeRename[] = [];
|
|
2030
2400
|
|
|
2031
2401
|
if (!hasConflicts(change)) {
|
|
2032
2402
|
// If there are no constraint violations, then tree changes apply.
|
|
2033
2403
|
const fieldDeltas = intoDeltaImpl(
|
|
2034
2404
|
change.fieldChanges,
|
|
2035
2405
|
change.nodeChanges,
|
|
2406
|
+
change.nodeAliases,
|
|
2036
2407
|
fieldKinds,
|
|
2037
|
-
global,
|
|
2038
|
-
rename,
|
|
2039
2408
|
);
|
|
2409
|
+
|
|
2410
|
+
const global: DeltaDetachedNodeChanges[] = [];
|
|
2411
|
+
for (const [[major, minor], nodeId] of change.rootNodes.nodeChanges.entries()) {
|
|
2412
|
+
global.push({
|
|
2413
|
+
id: { major, minor },
|
|
2414
|
+
fields: deltaFromNodeChange(
|
|
2415
|
+
nodeChangeFromId(change.nodeChanges, change.nodeAliases, nodeId),
|
|
2416
|
+
change.nodeChanges,
|
|
2417
|
+
change.nodeAliases,
|
|
2418
|
+
fieldKinds,
|
|
2419
|
+
),
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
const rename: DeltaDetachedNodeRename[] = [];
|
|
2424
|
+
for (const {
|
|
2425
|
+
start: oldId,
|
|
2426
|
+
value: newId,
|
|
2427
|
+
length,
|
|
2428
|
+
} of change.rootNodes.oldToNewId.entries()) {
|
|
2429
|
+
rename.push({
|
|
2430
|
+
count: length,
|
|
2431
|
+
oldId: makeDetachedNodeId(oldId.revision, oldId.localId),
|
|
2432
|
+
newId: makeDetachedNodeId(newId.revision, newId.localId),
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2040
2436
|
if (fieldDeltas.size > 0) {
|
|
2041
2437
|
rootDelta.fields = fieldDeltas;
|
|
2042
2438
|
}
|
|
@@ -2065,6 +2461,7 @@ export function intoDelta(
|
|
|
2065
2461
|
if (change.refreshers && change.refreshers.size > 0) {
|
|
2066
2462
|
rootDelta.refreshers = copyDetachedNodes(change.refreshers);
|
|
2067
2463
|
}
|
|
2464
|
+
|
|
2068
2465
|
return rootDelta;
|
|
2069
2466
|
}
|
|
2070
2467
|
|
|
@@ -2090,29 +2487,22 @@ function copyDetachedNodes(
|
|
|
2090
2487
|
function intoDeltaImpl(
|
|
2091
2488
|
change: FieldChangeMap,
|
|
2092
2489
|
nodeChanges: ChangeAtomIdBTree<NodeChangeset>,
|
|
2490
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
2093
2491
|
fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
|
|
2094
|
-
global: DeltaDetachedNodeChanges[],
|
|
2095
|
-
rename: DeltaDetachedNodeRename[],
|
|
2096
2492
|
): Map<FieldKey, DeltaFieldChanges> {
|
|
2097
2493
|
const delta: Map<FieldKey, DeltaFieldChanges> = new Map();
|
|
2098
2494
|
|
|
2099
2495
|
for (const [field, fieldChange] of change) {
|
|
2100
|
-
const
|
|
2101
|
-
local: fieldChanges,
|
|
2102
|
-
global: fieldGlobal,
|
|
2103
|
-
rename: fieldRename,
|
|
2104
|
-
} = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(
|
|
2496
|
+
const fieldDelta = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(
|
|
2105
2497
|
fieldChange.change,
|
|
2106
2498
|
(childChange): DeltaFieldMap => {
|
|
2107
|
-
const nodeChange = nodeChangeFromId(nodeChanges, childChange);
|
|
2108
|
-
return deltaFromNodeChange(nodeChange, nodeChanges,
|
|
2499
|
+
const nodeChange = nodeChangeFromId(nodeChanges, nodeAliases, childChange);
|
|
2500
|
+
return deltaFromNodeChange(nodeChange, nodeChanges, nodeAliases, fieldKinds);
|
|
2109
2501
|
},
|
|
2110
2502
|
);
|
|
2111
|
-
if (
|
|
2112
|
-
delta.set(field,
|
|
2503
|
+
if (fieldDelta !== undefined && fieldDelta.length > 0) {
|
|
2504
|
+
delta.set(field, fieldDelta);
|
|
2113
2505
|
}
|
|
2114
|
-
fieldGlobal?.forEach((c) => global.push(c));
|
|
2115
|
-
fieldRename?.forEach((r) => rename.push(r));
|
|
2116
2506
|
}
|
|
2117
2507
|
return delta;
|
|
2118
2508
|
}
|
|
@@ -2120,12 +2510,11 @@ function intoDeltaImpl(
|
|
|
2120
2510
|
function deltaFromNodeChange(
|
|
2121
2511
|
change: NodeChangeset,
|
|
2122
2512
|
nodeChanges: ChangeAtomIdBTree<NodeChangeset>,
|
|
2513
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
2123
2514
|
fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
|
|
2124
|
-
global: DeltaDetachedNodeChanges[],
|
|
2125
|
-
rename: DeltaDetachedNodeRename[],
|
|
2126
2515
|
): DeltaFieldMap {
|
|
2127
2516
|
if (change.fieldChanges !== undefined) {
|
|
2128
|
-
return intoDeltaImpl(change.fieldChanges, nodeChanges,
|
|
2517
|
+
return intoDeltaImpl(change.fieldChanges, nodeChanges, nodeAliases, fieldKinds);
|
|
2129
2518
|
}
|
|
2130
2519
|
// TODO: update the API to allow undefined to be returned here
|
|
2131
2520
|
return new Map();
|
|
@@ -2190,19 +2579,21 @@ export function getChangeHandler(
|
|
|
2190
2579
|
return getFieldKind(fieldKinds, kind).changeHandler;
|
|
2191
2580
|
}
|
|
2192
2581
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
interface CrossFieldTable<TFieldData> {
|
|
2196
|
-
srcTable: CrossFieldMap<unknown>;
|
|
2197
|
-
dstTable: CrossFieldMap<unknown>;
|
|
2198
|
-
srcDependents: CrossFieldMap<TFieldData>;
|
|
2199
|
-
dstDependents: CrossFieldMap<TFieldData>;
|
|
2200
|
-
invalidatedFields: Set<TFieldData>;
|
|
2201
|
-
}
|
|
2582
|
+
interface InvertTable {
|
|
2583
|
+
change: ModularChangeset;
|
|
2202
2584
|
|
|
2203
|
-
|
|
2585
|
+
// Entries are keyed on attach ID
|
|
2586
|
+
entries: CrossFieldMap<NodeId>;
|
|
2204
2587
|
originalFieldToContext: Map<FieldChange, InvertContext>;
|
|
2205
|
-
invertedNodeToParent: ChangeAtomIdBTree<
|
|
2588
|
+
invertedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
2589
|
+
invertRevision: RevisionTag;
|
|
2590
|
+
invalidatedFields: Set<FieldChange>;
|
|
2591
|
+
invertedRoots: RootNodeTable;
|
|
2592
|
+
|
|
2593
|
+
/**
|
|
2594
|
+
* Maps from attach ID in the inverted changeset to the corresponding detach ID in the base changeset.
|
|
2595
|
+
*/
|
|
2596
|
+
attachToDetachId: ChangeAtomIdRangeMap<ChangeAtomId>;
|
|
2206
2597
|
}
|
|
2207
2598
|
|
|
2208
2599
|
interface InvertContext {
|
|
@@ -2210,7 +2601,11 @@ interface InvertContext {
|
|
|
2210
2601
|
invertedField: FieldChange;
|
|
2211
2602
|
}
|
|
2212
2603
|
|
|
2213
|
-
interface RebaseTable
|
|
2604
|
+
interface RebaseTable {
|
|
2605
|
+
readonly rebaseVersion: RebaseVersion;
|
|
2606
|
+
|
|
2607
|
+
// Entries are keyed on attach ID
|
|
2608
|
+
readonly entries: CrossFieldMap<RebaseDetachedNodeEntry>;
|
|
2214
2609
|
readonly baseChange: ModularChangeset;
|
|
2215
2610
|
readonly newChange: ModularChangeset;
|
|
2216
2611
|
|
|
@@ -2219,10 +2614,13 @@ interface RebaseTable extends CrossFieldTable<FieldChange> {
|
|
|
2219
2614
|
* to the context for the field.
|
|
2220
2615
|
*/
|
|
2221
2616
|
readonly baseFieldToContext: Map<FieldChange, RebaseFieldContext>;
|
|
2617
|
+
readonly baseRoots: RootNodeTable;
|
|
2222
2618
|
readonly baseToRebasedNodeId: ChangeAtomIdBTree<NodeId>;
|
|
2223
2619
|
readonly rebasedFields: Set<FieldChange>;
|
|
2224
|
-
readonly rebasedNodeToParent: ChangeAtomIdBTree<
|
|
2225
|
-
readonly
|
|
2620
|
+
readonly rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
2621
|
+
readonly rebasedDetachLocations: ChangeAtomIdRangeMap<FieldId>;
|
|
2622
|
+
readonly movedDetaches: ChangeAtomIdRangeMap<boolean>;
|
|
2623
|
+
readonly rebasedRootNodes: RootNodeTable;
|
|
2226
2624
|
|
|
2227
2625
|
/**
|
|
2228
2626
|
* List of unprocessed (newId, baseId) pairs encountered so far.
|
|
@@ -2236,7 +2634,7 @@ interface RebaseTable extends CrossFieldTable<FieldChange> {
|
|
|
2236
2634
|
readonly fieldsWithUnattachedChild: Set<FieldChange>;
|
|
2237
2635
|
}
|
|
2238
2636
|
|
|
2239
|
-
type FieldIdKey = [RevisionTag | undefined, ChangesetLocalId | undefined, FieldKey];
|
|
2637
|
+
export type FieldIdKey = [RevisionTag | undefined, ChangesetLocalId | undefined, FieldKey];
|
|
2240
2638
|
|
|
2241
2639
|
interface RebaseFieldContext {
|
|
2242
2640
|
baseChange: FieldChange;
|
|
@@ -2248,32 +2646,43 @@ interface RebaseFieldContext {
|
|
|
2248
2646
|
* The set of node IDs in the base changeset which should be included in the rebased field,
|
|
2249
2647
|
* even if there is no corresponding node changeset in the new change.
|
|
2250
2648
|
*/
|
|
2251
|
-
baseNodeIds:
|
|
2649
|
+
baseNodeIds: ChangeAtomIdBTree<boolean>;
|
|
2252
2650
|
}
|
|
2253
2651
|
|
|
2254
2652
|
function newComposeTable(
|
|
2255
2653
|
baseChange: ModularChangeset,
|
|
2256
2654
|
newChange: ModularChangeset,
|
|
2257
|
-
|
|
2655
|
+
composedRootNodes: RootNodeTable,
|
|
2656
|
+
movedCrossFieldKeys: CrossFieldKeyTable,
|
|
2657
|
+
removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
|
|
2658
|
+
pendingCompositions: PendingCompositions,
|
|
2258
2659
|
): ComposeTable {
|
|
2259
2660
|
return {
|
|
2260
|
-
|
|
2661
|
+
rebaseVersion: Math.max(
|
|
2662
|
+
baseChange.rebaseVersion,
|
|
2663
|
+
newChange.rebaseVersion,
|
|
2664
|
+
) as RebaseVersion,
|
|
2665
|
+
entries: newDetachedEntryMap(),
|
|
2261
2666
|
baseChange,
|
|
2262
2667
|
newChange,
|
|
2263
2668
|
fieldToContext: new Map(),
|
|
2264
2669
|
newFieldToBaseField: new Map(),
|
|
2265
2670
|
newToBaseNodeId: newTupleBTree(),
|
|
2266
2671
|
composedNodes: new Set(),
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2672
|
+
movedNodeToParent: newTupleBTree(),
|
|
2673
|
+
composedRootNodes,
|
|
2674
|
+
movedCrossFieldKeys,
|
|
2675
|
+
removedCrossFieldKeys,
|
|
2676
|
+
renamesToDelete: newChangeAtomIdRangeMap(),
|
|
2677
|
+
pendingCompositions,
|
|
2273
2678
|
};
|
|
2274
2679
|
}
|
|
2275
2680
|
|
|
2276
|
-
interface ComposeTable
|
|
2681
|
+
interface ComposeTable {
|
|
2682
|
+
readonly rebaseVersion: RebaseVersion;
|
|
2683
|
+
|
|
2684
|
+
// Entries are keyed on detach ID
|
|
2685
|
+
readonly entries: ChangeAtomIdRangeMap<DetachedNodeEntry>;
|
|
2277
2686
|
readonly baseChange: ModularChangeset;
|
|
2278
2687
|
readonly newChange: ModularChangeset;
|
|
2279
2688
|
|
|
@@ -2284,7 +2693,11 @@ interface ComposeTable extends CrossFieldTable<FieldChange> {
|
|
|
2284
2693
|
readonly newFieldToBaseField: Map<FieldChange, FieldChange>;
|
|
2285
2694
|
readonly newToBaseNodeId: ChangeAtomIdBTree<NodeId>;
|
|
2286
2695
|
readonly composedNodes: Set<NodeChangeset>;
|
|
2287
|
-
readonly
|
|
2696
|
+
readonly movedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
2697
|
+
readonly composedRootNodes: RootNodeTable;
|
|
2698
|
+
readonly movedCrossFieldKeys: CrossFieldKeyTable;
|
|
2699
|
+
readonly removedCrossFieldKeys: CrossFieldRangeTable<boolean>;
|
|
2700
|
+
readonly renamesToDelete: ChangeAtomIdRangeMap<boolean>;
|
|
2288
2701
|
readonly pendingCompositions: PendingCompositions;
|
|
2289
2702
|
}
|
|
2290
2703
|
|
|
@@ -2299,11 +2712,6 @@ interface PendingCompositions {
|
|
|
2299
2712
|
* The set of fields in the base changeset which have been affected by a cross field effect.
|
|
2300
2713
|
*/
|
|
2301
2714
|
readonly affectedBaseFields: BTree<FieldIdKey, true>;
|
|
2302
|
-
|
|
2303
|
-
/**
|
|
2304
|
-
* The set of fields in the new changeset which have been affected by a cross field effect.
|
|
2305
|
-
*/
|
|
2306
|
-
readonly affectedNewFields: BTree<FieldIdKey, true>;
|
|
2307
2715
|
}
|
|
2308
2716
|
|
|
2309
2717
|
interface ComposeFieldContext {
|
|
@@ -2316,16 +2724,6 @@ interface ComposeFieldContext {
|
|
|
2316
2724
|
composedChange: FieldChange;
|
|
2317
2725
|
}
|
|
2318
2726
|
|
|
2319
|
-
function newCrossFieldTable<T>(): CrossFieldTable<T> {
|
|
2320
|
-
return {
|
|
2321
|
-
srcTable: newChangeAtomIdRangeMap(),
|
|
2322
|
-
dstTable: newChangeAtomIdRangeMap(),
|
|
2323
|
-
srcDependents: newChangeAtomIdRangeMap(),
|
|
2324
|
-
dstDependents: newChangeAtomIdRangeMap(),
|
|
2325
|
-
invalidatedFields: new Set(),
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
2727
|
interface ConstraintState {
|
|
2330
2728
|
violationCount: number;
|
|
2331
2729
|
}
|
|
@@ -2336,336 +2734,750 @@ function newConstraintState(violationCount: number): ConstraintState {
|
|
|
2336
2734
|
};
|
|
2337
2735
|
}
|
|
2338
2736
|
|
|
2339
|
-
|
|
2737
|
+
class InvertNodeManagerI implements InvertNodeManager {
|
|
2340
2738
|
public constructor(
|
|
2341
|
-
|
|
2342
|
-
private readonly
|
|
2343
|
-
protected readonly allowInval = true,
|
|
2739
|
+
private readonly table: InvertTable,
|
|
2740
|
+
private readonly fieldId: FieldId,
|
|
2344
2741
|
) {}
|
|
2345
2742
|
|
|
2346
|
-
public
|
|
2347
|
-
|
|
2348
|
-
revision: RevisionTag | undefined,
|
|
2349
|
-
id: ChangesetLocalId,
|
|
2743
|
+
public invertDetach(
|
|
2744
|
+
detachId: ChangeAtomId,
|
|
2350
2745
|
count: number,
|
|
2351
|
-
|
|
2352
|
-
|
|
2746
|
+
nodeChange: NodeId | undefined,
|
|
2747
|
+
newAttachId: ChangeAtomId,
|
|
2353
2748
|
): void {
|
|
2354
|
-
if (
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
lastChangedId - firstId + 1,
|
|
2363
|
-
);
|
|
2364
|
-
if (dependentEntry.value !== undefined) {
|
|
2365
|
-
this.crossFieldTable.invalidatedFields.add(dependentEntry.value);
|
|
2366
|
-
}
|
|
2749
|
+
if (nodeChange !== undefined) {
|
|
2750
|
+
assert(count === 1, "A node change should only affect one node");
|
|
2751
|
+
|
|
2752
|
+
const attachEntry = firstAttachIdFromDetachId(
|
|
2753
|
+
this.table.change.rootNodes,
|
|
2754
|
+
detachId,
|
|
2755
|
+
count,
|
|
2756
|
+
);
|
|
2367
2757
|
|
|
2368
|
-
|
|
2758
|
+
const attachFieldEntry = this.table.change.crossFieldKeys.getFirst(
|
|
2759
|
+
{ target: CrossFieldTarget.Destination, ...attachEntry.value },
|
|
2760
|
+
count,
|
|
2761
|
+
);
|
|
2762
|
+
|
|
2763
|
+
if (attachFieldEntry.value === undefined) {
|
|
2764
|
+
assignRootChange(
|
|
2765
|
+
this.table.invertedRoots,
|
|
2766
|
+
this.table.invertedNodeToParent,
|
|
2767
|
+
attachEntry.value,
|
|
2768
|
+
nodeChange,
|
|
2769
|
+
this.fieldId,
|
|
2770
|
+
this.table.change.rebaseVersion,
|
|
2771
|
+
);
|
|
2772
|
+
} else {
|
|
2773
|
+
setInCrossFieldMap(this.table.entries, attachEntry.value, count, nodeChange);
|
|
2774
|
+
this.table.invalidatedFields.add(
|
|
2775
|
+
fieldChangeFromId(this.table.change, attachFieldEntry.value),
|
|
2776
|
+
);
|
|
2369
2777
|
}
|
|
2370
2778
|
}
|
|
2371
|
-
setInCrossFieldMap(this.getMap(target), revision, id, count, newValue);
|
|
2372
|
-
}
|
|
2373
2779
|
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
count: number,
|
|
2379
|
-
addDependency: boolean,
|
|
2380
|
-
): RangeQueryResult<ChangeAtomId, unknown> {
|
|
2381
|
-
if (addDependency) {
|
|
2382
|
-
// We assume that if there is already an entry for this ID it is because
|
|
2383
|
-
// a field handler has called compose on the same node multiple times.
|
|
2384
|
-
// In this case we only want to update the latest version, so we overwrite the dependency.
|
|
2385
|
-
setInCrossFieldMap(
|
|
2386
|
-
this.getDependents(target),
|
|
2387
|
-
revision,
|
|
2388
|
-
id,
|
|
2780
|
+
if (!areEqualChangeAtomIds(detachId, newAttachId)) {
|
|
2781
|
+
for (const entry of doesChangeAttachNodes(
|
|
2782
|
+
this.table.change.crossFieldKeys,
|
|
2783
|
+
detachId,
|
|
2389
2784
|
count,
|
|
2390
|
-
|
|
2391
|
-
|
|
2785
|
+
)) {
|
|
2786
|
+
if (!entry.value) {
|
|
2787
|
+
this.table.attachToDetachId.set(newAttachId, count, detachId);
|
|
2788
|
+
this.table.invertedRoots.detachLocations.set(detachId, count, this.fieldId);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2392
2791
|
}
|
|
2393
|
-
return getFirstFromCrossFieldMap(this.getMap(target), revision, id, count);
|
|
2394
2792
|
}
|
|
2395
2793
|
|
|
2396
|
-
public
|
|
2397
|
-
|
|
2398
|
-
public abstract moveKey(
|
|
2399
|
-
target: CrossFieldTarget,
|
|
2400
|
-
revision: RevisionTag | undefined,
|
|
2401
|
-
id: ChangesetLocalId,
|
|
2794
|
+
public invertAttach(
|
|
2795
|
+
attachId: ChangeAtomId,
|
|
2402
2796
|
count: number,
|
|
2403
|
-
):
|
|
2404
|
-
|
|
2405
|
-
private getMap(target: CrossFieldTarget): CrossFieldMap<unknown> {
|
|
2406
|
-
return target === CrossFieldTarget.Source
|
|
2407
|
-
? this.crossFieldTable.srcTable
|
|
2408
|
-
: this.crossFieldTable.dstTable;
|
|
2409
|
-
}
|
|
2797
|
+
): RangeQueryResult<DetachedNodeEntry> {
|
|
2798
|
+
let countToProcess = count;
|
|
2410
2799
|
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
}
|
|
2800
|
+
const detachIdEntry = firstDetachIdFromAttachId(
|
|
2801
|
+
this.table.change.rootNodes,
|
|
2802
|
+
attachId,
|
|
2803
|
+
countToProcess,
|
|
2804
|
+
);
|
|
2417
2805
|
|
|
2418
|
-
|
|
2419
|
-
public constructor(
|
|
2420
|
-
table: InvertTable,
|
|
2421
|
-
field: FieldChange,
|
|
2422
|
-
private readonly fieldId: FieldId,
|
|
2423
|
-
allowInval = true,
|
|
2424
|
-
) {
|
|
2425
|
-
super(table, field, allowInval);
|
|
2426
|
-
}
|
|
2806
|
+
countToProcess = detachIdEntry.length;
|
|
2427
2807
|
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2808
|
+
const detachEntry = getFirstFieldForCrossFieldKey(
|
|
2809
|
+
this.table.change,
|
|
2810
|
+
{ target: CrossFieldTarget.Source, ...detachIdEntry.value },
|
|
2811
|
+
countToProcess,
|
|
2812
|
+
);
|
|
2813
|
+
countToProcess = detachEntry.length;
|
|
2814
|
+
|
|
2815
|
+
let result: RangeQueryResult<DetachedNodeEntry>;
|
|
2816
|
+
if (detachEntry.value === undefined) {
|
|
2817
|
+
// This node is detached in the input context of the original change.
|
|
2818
|
+
const nodeIdEntry = rangeQueryChangeAtomIdMap(
|
|
2819
|
+
this.table.change.rootNodes.nodeChanges,
|
|
2820
|
+
detachIdEntry.value,
|
|
2821
|
+
countToProcess,
|
|
2822
|
+
);
|
|
2431
2823
|
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2824
|
+
countToProcess = nodeIdEntry.length;
|
|
2825
|
+
result = {
|
|
2826
|
+
value: { nodeChange: nodeIdEntry.value, detachId: detachIdEntry.value },
|
|
2827
|
+
length: countToProcess,
|
|
2828
|
+
};
|
|
2829
|
+
} else {
|
|
2830
|
+
const moveEntry = this.table.entries.getFirst(attachId, countToProcess);
|
|
2831
|
+
result = { ...moveEntry, value: { nodeChange: moveEntry.value } };
|
|
2832
|
+
}
|
|
2440
2833
|
|
|
2441
|
-
|
|
2442
|
-
|
|
2834
|
+
if (result.value?.nodeChange !== undefined) {
|
|
2835
|
+
setInChangeAtomIdMap(this.table.invertedNodeToParent, result.value.nodeChange, {
|
|
2836
|
+
field: this.fieldId,
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
return result;
|
|
2443
2840
|
}
|
|
2444
2841
|
}
|
|
2445
2842
|
|
|
2446
|
-
class
|
|
2843
|
+
class RebaseNodeManagerI implements RebaseNodeManager {
|
|
2447
2844
|
public constructor(
|
|
2448
|
-
table: RebaseTable,
|
|
2449
|
-
currentField: FieldChange,
|
|
2845
|
+
private readonly table: RebaseTable,
|
|
2450
2846
|
private readonly fieldId: FieldId,
|
|
2451
|
-
allowInval = true,
|
|
2452
|
-
) {
|
|
2453
|
-
super(table, currentField, allowInval);
|
|
2454
|
-
}
|
|
2847
|
+
private readonly allowInval: boolean = true,
|
|
2848
|
+
) {}
|
|
2455
2849
|
|
|
2456
|
-
public
|
|
2457
|
-
|
|
2458
|
-
revision: RevisionTag | undefined,
|
|
2459
|
-
id: ChangesetLocalId,
|
|
2850
|
+
public getNewChangesForBaseAttach(
|
|
2851
|
+
baseAttachId: ChangeAtomId,
|
|
2460
2852
|
count: number,
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
): void {
|
|
2464
|
-
if (invalidateDependents && this.allowInval) {
|
|
2465
|
-
const newFieldIds = getFieldsForCrossFieldKey(
|
|
2466
|
-
this.table.newChange,
|
|
2467
|
-
{
|
|
2468
|
-
target,
|
|
2469
|
-
revision,
|
|
2470
|
-
localId: id,
|
|
2471
|
-
},
|
|
2472
|
-
count,
|
|
2473
|
-
);
|
|
2853
|
+
): RangeQueryResult<RebaseDetachedNodeEntry | undefined> {
|
|
2854
|
+
let countToProcess = count;
|
|
2474
2855
|
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2856
|
+
const detachEntry = firstDetachIdFromAttachId(
|
|
2857
|
+
this.table.baseChange.rootNodes,
|
|
2858
|
+
baseAttachId,
|
|
2859
|
+
countToProcess,
|
|
2860
|
+
);
|
|
2479
2861
|
|
|
2480
|
-
|
|
2481
|
-
this.table.baseChange,
|
|
2482
|
-
{
|
|
2483
|
-
target,
|
|
2484
|
-
revision,
|
|
2485
|
-
localId: id,
|
|
2486
|
-
},
|
|
2487
|
-
count,
|
|
2488
|
-
);
|
|
2862
|
+
countToProcess = detachEntry.length;
|
|
2489
2863
|
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2864
|
+
const nodeEntry = rangeQueryChangeAtomIdMap(
|
|
2865
|
+
this.table.newChange.rootNodes.nodeChanges,
|
|
2866
|
+
detachEntry.value,
|
|
2867
|
+
countToProcess,
|
|
2868
|
+
);
|
|
2494
2869
|
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2870
|
+
countToProcess = nodeEntry.length;
|
|
2871
|
+
const newNodeId = nodeEntry.value;
|
|
2872
|
+
|
|
2873
|
+
const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(
|
|
2874
|
+
detachEntry.value,
|
|
2875
|
+
countToProcess,
|
|
2876
|
+
);
|
|
2877
|
+
|
|
2878
|
+
countToProcess = newRenameEntry.length;
|
|
2879
|
+
|
|
2880
|
+
let result: RangeQueryResult<DetachedNodeEntry | undefined>;
|
|
2881
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
2882
|
+
if (newNodeId !== undefined || newRenameEntry.value !== undefined) {
|
|
2883
|
+
result = {
|
|
2884
|
+
...newRenameEntry,
|
|
2885
|
+
value: { detachId: newRenameEntry.value, nodeChange: newNodeId },
|
|
2886
|
+
};
|
|
2887
|
+
} else {
|
|
2888
|
+
// This handles the case where the base changeset has moved these nodes,
|
|
2889
|
+
// meaning they were attached in the input context of the base changeset.
|
|
2890
|
+
result = this.table.entries.getFirst(baseAttachId, countToProcess);
|
|
2501
2891
|
}
|
|
2502
2892
|
|
|
2503
|
-
|
|
2504
|
-
|
|
2893
|
+
// TODO: Consider moving these two checks into a separate method so that this function has no side effects.
|
|
2894
|
+
if (result.value?.detachId !== undefined) {
|
|
2895
|
+
this.table.rebasedDetachLocations.set(
|
|
2896
|
+
result.value.detachId,
|
|
2897
|
+
result.length,
|
|
2898
|
+
this.fieldId,
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
if (result.value?.nodeChange !== undefined) {
|
|
2903
|
+
setInChangeAtomIdMap(this.table.rebasedNodeToParent, result.value.nodeChange, {
|
|
2904
|
+
field: this.fieldId,
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2505
2907
|
|
|
2506
|
-
|
|
2507
|
-
setInChangeAtomIdMap(this.table.rebasedNodeToParent, id, this.fieldId);
|
|
2908
|
+
return result;
|
|
2508
2909
|
}
|
|
2509
2910
|
|
|
2510
|
-
public
|
|
2511
|
-
|
|
2512
|
-
revision: RevisionTag | undefined,
|
|
2513
|
-
id: ChangesetLocalId,
|
|
2911
|
+
public rebaseOverDetach(
|
|
2912
|
+
baseDetachId: ChangeAtomId,
|
|
2514
2913
|
count: number,
|
|
2914
|
+
newDetachId: ChangeAtomId | undefined,
|
|
2915
|
+
nodeChange: NodeId | undefined,
|
|
2916
|
+
cellRename?: ChangeAtomId,
|
|
2515
2917
|
): void {
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2918
|
+
let countToProcess = count;
|
|
2919
|
+
const attachIdEntry = firstAttachIdFromDetachId(
|
|
2920
|
+
this.table.baseRoots,
|
|
2921
|
+
baseDetachId,
|
|
2922
|
+
countToProcess,
|
|
2520
2923
|
);
|
|
2521
|
-
|
|
2924
|
+
const baseAttachId = attachIdEntry.value;
|
|
2925
|
+
countToProcess = attachIdEntry.length;
|
|
2522
2926
|
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2927
|
+
const attachFieldEntry = getFirstFieldForCrossFieldKey(
|
|
2928
|
+
this.table.baseChange,
|
|
2929
|
+
{ ...baseAttachId, target: CrossFieldTarget.Destination },
|
|
2930
|
+
countToProcess,
|
|
2931
|
+
);
|
|
2932
|
+
countToProcess = attachFieldEntry.length;
|
|
2933
|
+
|
|
2934
|
+
const detachedMoveEntry = this.table.baseChange.rootNodes.outputDetachLocations.getFirst(
|
|
2935
|
+
baseDetachId,
|
|
2936
|
+
countToProcess,
|
|
2937
|
+
);
|
|
2938
|
+
countToProcess = detachedMoveEntry.length;
|
|
2939
|
+
|
|
2940
|
+
const destinationField = attachFieldEntry.value ?? detachedMoveEntry.value;
|
|
2941
|
+
if (destinationField !== undefined) {
|
|
2942
|
+
// The base detach is part of a move (or move of detach location) in the base changeset.
|
|
2943
|
+
setInCrossFieldMap(this.table.entries, baseAttachId, countToProcess, {
|
|
2944
|
+
nodeChange,
|
|
2945
|
+
detachId: newDetachId,
|
|
2946
|
+
cellRename,
|
|
2947
|
+
});
|
|
2948
|
+
|
|
2949
|
+
if (nodeChange !== undefined || newDetachId !== undefined) {
|
|
2950
|
+
this.invalidateBaseFields([destinationField]);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
if (attachFieldEntry.value === undefined) {
|
|
2955
|
+
// These nodes are detached in the output context of the base changeset.
|
|
2956
|
+
if (nodeChange !== undefined) {
|
|
2957
|
+
assignRootChange(
|
|
2958
|
+
this.table.rebasedRootNodes,
|
|
2959
|
+
this.table.rebasedNodeToParent,
|
|
2960
|
+
baseAttachId,
|
|
2961
|
+
nodeChange,
|
|
2962
|
+
this.fieldId,
|
|
2963
|
+
this.table.rebaseVersion,
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
if (newDetachId !== undefined) {
|
|
2968
|
+
addNodeRename(
|
|
2969
|
+
this.table.rebasedRootNodes,
|
|
2970
|
+
baseAttachId,
|
|
2971
|
+
newDetachId,
|
|
2972
|
+
countToProcess,
|
|
2973
|
+
this.fieldId,
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
if (newDetachId !== undefined) {
|
|
2979
|
+
this.table.movedDetaches.set(newDetachId, countToProcess, true);
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
if (countToProcess < count) {
|
|
2983
|
+
const remainingCount = count - countToProcess;
|
|
2984
|
+
|
|
2985
|
+
const nextDetachId =
|
|
2986
|
+
newDetachId === undefined
|
|
2987
|
+
? undefined
|
|
2988
|
+
: offsetChangeAtomId(newDetachId, countToProcess);
|
|
2989
|
+
|
|
2990
|
+
this.rebaseOverDetach(
|
|
2991
|
+
offsetChangeAtomId(baseDetachId, countToProcess),
|
|
2992
|
+
remainingCount,
|
|
2993
|
+
nextDetachId,
|
|
2994
|
+
nodeChange,
|
|
2995
|
+
);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
public addDetach(id: ChangeAtomId, count: number): void {
|
|
3000
|
+
this.table.rebasedDetachLocations.set(id, count, this.fieldId);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
public removeDetach(id: ChangeAtomId, count: number): void {
|
|
3004
|
+
this.table.movedDetaches.set(id, count, true);
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
public doesBaseAttachNodes(
|
|
3008
|
+
id: ChangeAtomId,
|
|
3009
|
+
count: number,
|
|
3010
|
+
): RangeQueryEntry<ChangeAtomId, boolean> {
|
|
3011
|
+
let countToProcess = count;
|
|
3012
|
+
const attachEntry = getFirstAttachField(
|
|
3013
|
+
this.table.baseChange.crossFieldKeys,
|
|
3014
|
+
id,
|
|
3015
|
+
countToProcess,
|
|
3016
|
+
);
|
|
3017
|
+
|
|
3018
|
+
countToProcess = attachEntry.length;
|
|
3019
|
+
return { start: id, value: attachEntry.value !== undefined, length: countToProcess };
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
public getBaseRename(
|
|
3023
|
+
id: ChangeAtomId,
|
|
3024
|
+
count: number,
|
|
3025
|
+
): RangeQueryResult<ChangeAtomId | undefined> {
|
|
3026
|
+
return this.table.baseChange.rootNodes.oldToNewId.getFirst(id, count);
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
public getNewRenameForBaseRename(
|
|
3030
|
+
baseRenameTo: ChangeAtomId,
|
|
3031
|
+
count: number,
|
|
3032
|
+
): RangeQueryResult<ChangeAtomId | undefined> {
|
|
3033
|
+
let countToProcess = count;
|
|
3034
|
+
const inputEntry = firstDetachIdFromAttachId(
|
|
3035
|
+
this.table.baseChange.rootNodes,
|
|
3036
|
+
baseRenameTo,
|
|
3037
|
+
countToProcess,
|
|
3038
|
+
);
|
|
3039
|
+
|
|
3040
|
+
const attachEntry = getFirstAttachField(
|
|
3041
|
+
this.table.baseChange.crossFieldKeys,
|
|
3042
|
+
baseRenameTo,
|
|
3043
|
+
countToProcess,
|
|
3044
|
+
);
|
|
3045
|
+
|
|
3046
|
+
countToProcess = attachEntry.length;
|
|
3047
|
+
if (attachEntry.value !== undefined) {
|
|
3048
|
+
// These nodes are attached in the output context of the base changeset.
|
|
3049
|
+
return { value: undefined, length: countToProcess };
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
countToProcess = inputEntry.length;
|
|
3053
|
+
const inputId = inputEntry.value;
|
|
3054
|
+
|
|
3055
|
+
const moveEntry = this.table.entries.getFirst(inputId, countToProcess);
|
|
3056
|
+
|
|
3057
|
+
countToProcess = moveEntry.length;
|
|
3058
|
+
if (moveEntry.value !== undefined) {
|
|
3059
|
+
return { ...moveEntry, value: moveEntry.value.cellRename ?? moveEntry.value.detachId };
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
return this.table.newChange.rootNodes.oldToNewId.getFirst(inputId, countToProcess);
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
private invalidateBaseFields(fields: FieldId[]): void {
|
|
3066
|
+
if (this.allowInval) {
|
|
3067
|
+
for (const fieldId of fields) {
|
|
3068
|
+
this.table.affectedBaseFields.set(fieldIdKeyFromFieldId(fieldId), true);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
function assignRootChange(
|
|
3075
|
+
table: RootNodeTable,
|
|
3076
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation> | undefined,
|
|
3077
|
+
detachId: ChangeAtomId,
|
|
3078
|
+
nodeId: NodeId,
|
|
3079
|
+
detachLocation: FieldId | undefined,
|
|
3080
|
+
rebaseVersion: RebaseVersion,
|
|
3081
|
+
): void {
|
|
3082
|
+
assert(
|
|
3083
|
+
rebaseVersion >= 2 || detachLocation !== undefined,
|
|
3084
|
+
"All root changes need a detach location to support compatibility with older client versions",
|
|
3085
|
+
);
|
|
3086
|
+
|
|
3087
|
+
setInChangeAtomIdMap(table.nodeChanges, detachId, nodeId);
|
|
3088
|
+
|
|
3089
|
+
if (nodeToParent !== undefined) {
|
|
3090
|
+
setInChangeAtomIdMap(nodeToParent, nodeId, { root: detachId });
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
table.detachLocations.set(detachId, 1, detachLocation);
|
|
2526
3094
|
}
|
|
2527
3095
|
|
|
2528
|
-
|
|
2529
|
-
class ComposeManager extends CrossFieldManagerI<FieldChange> {
|
|
3096
|
+
class ComposeNodeManagerI implements ComposeNodeManager {
|
|
2530
3097
|
public constructor(
|
|
2531
|
-
table: ComposeTable,
|
|
2532
|
-
currentField: FieldChange,
|
|
3098
|
+
private readonly table: ComposeTable,
|
|
2533
3099
|
private readonly fieldId: FieldId,
|
|
2534
|
-
allowInval = true,
|
|
2535
|
-
) {
|
|
2536
|
-
|
|
3100
|
+
private readonly allowInval: boolean = true,
|
|
3101
|
+
) {}
|
|
3102
|
+
|
|
3103
|
+
public getNewChangesForBaseDetach(
|
|
3104
|
+
baseDetachId: ChangeAtomId,
|
|
3105
|
+
count: number,
|
|
3106
|
+
): RangeQueryResult<DetachedNodeEntry | undefined> {
|
|
3107
|
+
let countToProcess = count;
|
|
3108
|
+
|
|
3109
|
+
const baseAttachEntry = getFirstFieldForCrossFieldKey(
|
|
3110
|
+
this.table.baseChange,
|
|
3111
|
+
{ target: CrossFieldTarget.Destination, ...baseDetachId },
|
|
3112
|
+
countToProcess,
|
|
3113
|
+
);
|
|
3114
|
+
|
|
3115
|
+
countToProcess = baseAttachEntry.length;
|
|
3116
|
+
|
|
3117
|
+
let result: RangeQueryResult<DetachedNodeEntry | undefined>;
|
|
3118
|
+
if (baseAttachEntry.value === undefined) {
|
|
3119
|
+
// The detached nodes are still detached in the new change's input context.
|
|
3120
|
+
const rootEntry = rangeQueryChangeAtomIdMap(
|
|
3121
|
+
this.table.newChange.rootNodes.nodeChanges,
|
|
3122
|
+
baseDetachId,
|
|
3123
|
+
countToProcess,
|
|
3124
|
+
);
|
|
3125
|
+
|
|
3126
|
+
countToProcess = rootEntry.length;
|
|
3127
|
+
|
|
3128
|
+
const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(
|
|
3129
|
+
baseDetachId,
|
|
3130
|
+
countToProcess,
|
|
3131
|
+
);
|
|
3132
|
+
|
|
3133
|
+
countToProcess = newRenameEntry.length;
|
|
3134
|
+
|
|
3135
|
+
result = {
|
|
3136
|
+
value: { nodeChange: rootEntry.value, detachId: newRenameEntry.value },
|
|
3137
|
+
length: countToProcess,
|
|
3138
|
+
};
|
|
3139
|
+
} else {
|
|
3140
|
+
// The base detach was part of a move.
|
|
3141
|
+
// We check if we've previously seen a node change at the move destination.
|
|
3142
|
+
const entry = this.table.entries.getFirst(baseDetachId, countToProcess);
|
|
3143
|
+
result = { value: entry.value, length: entry.length };
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
// TODO: Consider moving this to a separate method so that this method can be side-effect free.
|
|
3147
|
+
if (result.value?.nodeChange !== undefined) {
|
|
3148
|
+
setInChangeAtomIdMap(this.table.movedNodeToParent, result.value.nodeChange, {
|
|
3149
|
+
field: this.fieldId,
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
return result;
|
|
2537
3154
|
}
|
|
2538
3155
|
|
|
2539
|
-
public
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
id: ChangesetLocalId,
|
|
3156
|
+
public composeAttachDetach(
|
|
3157
|
+
baseAttachId: ChangeAtomId,
|
|
3158
|
+
newDetachId: ChangeAtomId,
|
|
2543
3159
|
count: number,
|
|
2544
|
-
newValue: unknown,
|
|
2545
|
-
invalidateDependents: boolean,
|
|
2546
3160
|
): void {
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
3161
|
+
let countToProcess = count;
|
|
3162
|
+
|
|
3163
|
+
const newAttachEntry = getFirstAttachField(
|
|
3164
|
+
this.table.newChange.crossFieldKeys,
|
|
3165
|
+
newDetachId,
|
|
3166
|
+
countToProcess,
|
|
3167
|
+
);
|
|
3168
|
+
|
|
3169
|
+
countToProcess = newAttachEntry.length;
|
|
3170
|
+
|
|
3171
|
+
// Both changes can have the same ID if they came from inverse changesets.
|
|
3172
|
+
// If the new detach is part of a move,
|
|
3173
|
+
// then both input changesets contain the attach cross-field key for this ID.
|
|
3174
|
+
// The new attach may still exist in the composed changeset so we do not remove it here.
|
|
3175
|
+
// The new attach will typically cancel with a base detach,
|
|
3176
|
+
// in which case the cross-field key will be removed in `composeDetachAttach`.
|
|
3177
|
+
const hasNewAttachWithBaseAttachId =
|
|
3178
|
+
areEqualChangeAtomIds(baseAttachId, newDetachId) && newAttachEntry.value !== undefined;
|
|
3179
|
+
|
|
3180
|
+
if (!hasNewAttachWithBaseAttachId) {
|
|
3181
|
+
this.table.removedCrossFieldKeys.set(
|
|
3182
|
+
{ ...baseAttachId, target: CrossFieldTarget.Destination },
|
|
3183
|
+
countToProcess,
|
|
3184
|
+
true,
|
|
2556
3185
|
);
|
|
3186
|
+
}
|
|
2557
3187
|
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
3188
|
+
const baseDetachEntry = getFirstDetachField(
|
|
3189
|
+
this.table.baseChange.crossFieldKeys,
|
|
3190
|
+
baseAttachId,
|
|
3191
|
+
countToProcess,
|
|
3192
|
+
);
|
|
3193
|
+
|
|
3194
|
+
countToProcess = baseDetachEntry.length;
|
|
3195
|
+
|
|
3196
|
+
const baseRootIdEntry = firstDetachIdFromAttachId(
|
|
3197
|
+
this.table.baseChange.rootNodes,
|
|
3198
|
+
baseAttachId,
|
|
3199
|
+
countToProcess,
|
|
3200
|
+
);
|
|
3201
|
+
countToProcess = baseRootIdEntry.length;
|
|
3202
|
+
|
|
3203
|
+
const baseDetachId = baseRootIdEntry.value;
|
|
3204
|
+
|
|
3205
|
+
if (baseDetachEntry.value === undefined) {
|
|
3206
|
+
const baseDetachLocationEntry = this.table.baseChange.rootNodes.detachLocations.getFirst(
|
|
3207
|
+
baseDetachId,
|
|
3208
|
+
countToProcess,
|
|
3209
|
+
);
|
|
3210
|
+
countToProcess = baseDetachLocationEntry.length;
|
|
3211
|
+
|
|
3212
|
+
// These nodes were detached in the base change's input context,
|
|
3213
|
+
// so the net effect of the two changes is a rename.
|
|
3214
|
+
appendNodeRename(
|
|
3215
|
+
this.table.composedRootNodes,
|
|
3216
|
+
baseAttachId,
|
|
3217
|
+
newDetachId,
|
|
3218
|
+
baseDetachEntry.length,
|
|
3219
|
+
this.table.baseChange.rootNodes,
|
|
3220
|
+
baseDetachLocationEntry.value ?? this.fieldId,
|
|
3221
|
+
);
|
|
3222
|
+
|
|
3223
|
+
this.table.removedCrossFieldKeys.set(
|
|
3224
|
+
{ ...newDetachId, target: CrossFieldTarget.Source },
|
|
3225
|
+
countToProcess,
|
|
3226
|
+
true,
|
|
3227
|
+
);
|
|
3228
|
+
} else {
|
|
3229
|
+
// The base change moves these nodes.
|
|
3230
|
+
const prevEntry =
|
|
3231
|
+
this.table.entries.getFirst(baseAttachId, baseDetachEntry.length).value ?? {};
|
|
3232
|
+
|
|
3233
|
+
this.table.entries.set(baseAttachId, baseDetachEntry.length, {
|
|
3234
|
+
...prevEntry,
|
|
3235
|
+
detachId: newDetachId,
|
|
3236
|
+
});
|
|
3237
|
+
|
|
3238
|
+
// The new detach will replace the base detach, so we remove the key for the base detach, unless they have the same ID.
|
|
3239
|
+
if (!areEqualChangeAtomIds(baseAttachId, newDetachId)) {
|
|
3240
|
+
this.table.removedCrossFieldKeys.set(
|
|
3241
|
+
{ ...baseAttachId, target: CrossFieldTarget.Source },
|
|
3242
|
+
countToProcess,
|
|
3243
|
+
true,
|
|
2574
3244
|
);
|
|
3245
|
+
}
|
|
2575
3246
|
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
3247
|
+
this.table.movedCrossFieldKeys.set(
|
|
3248
|
+
{ ...newDetachId, target: CrossFieldTarget.Source },
|
|
3249
|
+
countToProcess,
|
|
3250
|
+
baseDetachEntry.value,
|
|
3251
|
+
);
|
|
3252
|
+
|
|
3253
|
+
this.invalidateBaseFields([baseDetachEntry.value]);
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
if (newAttachEntry.value === undefined) {
|
|
3257
|
+
const newOutputDetachLocationEntry =
|
|
3258
|
+
this.table.newChange.rootNodes.outputDetachLocations.getFirst(
|
|
3259
|
+
newDetachId,
|
|
3260
|
+
countToProcess,
|
|
2579
3261
|
);
|
|
2580
3262
|
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
3263
|
+
countToProcess = newOutputDetachLocationEntry.length;
|
|
3264
|
+
|
|
3265
|
+
this.table.composedRootNodes.outputDetachLocations.set(
|
|
3266
|
+
newDetachId,
|
|
3267
|
+
countToProcess,
|
|
3268
|
+
newOutputDetachLocationEntry.value ?? this.fieldId,
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
if (countToProcess < count) {
|
|
3273
|
+
const remainingCount = count - countToProcess;
|
|
3274
|
+
this.composeAttachDetach(
|
|
3275
|
+
offsetChangeAtomId(baseAttachId, countToProcess),
|
|
3276
|
+
offsetChangeAtomId(newDetachId, countToProcess),
|
|
3277
|
+
remainingCount,
|
|
3278
|
+
);
|
|
2588
3279
|
}
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
public sendNewChangesToBaseSourceLocation(
|
|
3283
|
+
baseAttachId: ChangeAtomId,
|
|
3284
|
+
newChanges: NodeId,
|
|
3285
|
+
): void {
|
|
3286
|
+
const { value: baseDetachId } = firstDetachIdFromAttachId(
|
|
3287
|
+
this.table.baseChange.rootNodes,
|
|
3288
|
+
baseAttachId,
|
|
3289
|
+
1,
|
|
3290
|
+
);
|
|
3291
|
+
|
|
3292
|
+
const detachFields = getFieldsForCrossFieldKey(
|
|
3293
|
+
this.table.baseChange,
|
|
3294
|
+
{
|
|
3295
|
+
...baseDetachId,
|
|
3296
|
+
target: CrossFieldTarget.Source,
|
|
3297
|
+
},
|
|
3298
|
+
1,
|
|
3299
|
+
);
|
|
3300
|
+
|
|
3301
|
+
if (detachFields.length > 0) {
|
|
3302
|
+
// The base attach is part of a move in the base changeset.
|
|
3303
|
+
const prevEntry = this.table.entries.getFirst(baseDetachId, 1).value ?? {};
|
|
3304
|
+
this.table.entries.set(baseDetachId, 1, { ...prevEntry, nodeChange: newChanges });
|
|
3305
|
+
|
|
3306
|
+
if (newChanges !== undefined) {
|
|
3307
|
+
this.invalidateBaseFields(detachFields);
|
|
3308
|
+
}
|
|
3309
|
+
} else {
|
|
3310
|
+
const baseNodeId = getFromChangeAtomIdMap(
|
|
3311
|
+
this.table.baseChange.rootNodes.nodeChanges,
|
|
3312
|
+
baseDetachId,
|
|
3313
|
+
);
|
|
2589
3314
|
|
|
2590
|
-
|
|
3315
|
+
if (baseNodeId === undefined) {
|
|
3316
|
+
assignRootChange(
|
|
3317
|
+
this.table.composedRootNodes,
|
|
3318
|
+
this.table.movedNodeToParent,
|
|
3319
|
+
baseDetachId,
|
|
3320
|
+
newChanges,
|
|
3321
|
+
this.fieldId,
|
|
3322
|
+
this.table.rebaseVersion,
|
|
3323
|
+
);
|
|
3324
|
+
} else {
|
|
3325
|
+
addNodesToCompose(this.table, baseNodeId, newChanges);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
2591
3328
|
}
|
|
2592
3329
|
|
|
2593
|
-
|
|
2594
|
-
|
|
3330
|
+
private areSameNodes(
|
|
3331
|
+
baseDetachId: ChangeAtomId,
|
|
3332
|
+
newAttachId: ChangeAtomId,
|
|
3333
|
+
count: number,
|
|
3334
|
+
): RangeQueryResult<boolean> {
|
|
3335
|
+
const renamedDetachEntry = firstAttachIdFromDetachId(
|
|
3336
|
+
this.table.composedRootNodes,
|
|
3337
|
+
baseDetachId,
|
|
3338
|
+
count,
|
|
3339
|
+
);
|
|
3340
|
+
|
|
3341
|
+
const isReattachOfSameNodes = areEqualChangeAtomIds(renamedDetachEntry.value, newAttachId);
|
|
3342
|
+
return { ...renamedDetachEntry, value: isReattachOfSameNodes };
|
|
2595
3343
|
}
|
|
2596
3344
|
|
|
2597
|
-
public
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
id: ChangesetLocalId,
|
|
3345
|
+
public composeDetachAttach(
|
|
3346
|
+
baseDetachId: ChangeAtomId,
|
|
3347
|
+
newAttachId: ChangeAtomId,
|
|
2601
3348
|
count: number,
|
|
3349
|
+
composeToPin: boolean,
|
|
2602
3350
|
): void {
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
3351
|
+
const areSameEntry = this.areSameNodes(baseDetachId, newAttachId, count);
|
|
3352
|
+
|
|
3353
|
+
const countToProcess = areSameEntry.length;
|
|
3354
|
+
if (areSameEntry.value) {
|
|
3355
|
+
// These nodes have been moved back to their original location, so the composed changeset should not have any renames for them.
|
|
3356
|
+
// Note that deleting the rename from `this.table.composedRootNodes` would change the result of this method
|
|
3357
|
+
// if it were rerun due to the field being invalidated, so we instead record that the rename should be deleted later.
|
|
3358
|
+
this.table.renamesToDelete.set(baseDetachId, countToProcess, true);
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
if (composeToPin) {
|
|
3362
|
+
this.table.movedCrossFieldKeys.set(
|
|
3363
|
+
{ target: CrossFieldTarget.Source, ...newAttachId },
|
|
3364
|
+
countToProcess,
|
|
3365
|
+
this.fieldId,
|
|
3366
|
+
);
|
|
3367
|
+
|
|
3368
|
+
if (!areEqualChangeAtomIds(baseDetachId, newAttachId)) {
|
|
3369
|
+
// The pin will have `newAttachId` as both its detach and attach ID.
|
|
3370
|
+
// So we remove `baseDetachId` unless that is equal to the pin's detach ID.
|
|
3371
|
+
this.table.removedCrossFieldKeys.set(
|
|
3372
|
+
{ target: CrossFieldTarget.Source, ...baseDetachId },
|
|
3373
|
+
countToProcess,
|
|
3374
|
+
true,
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
// Note that while change2 should already have this key, change1 may have a rollback for the same ID in a different location.
|
|
3379
|
+
// In that case, change1's attach should be canceled out by a detach from change2.
|
|
3380
|
+
// Here we make sure that the composed change has the correct location (this field) for the attach ID.
|
|
3381
|
+
this.table.movedCrossFieldKeys.set(
|
|
3382
|
+
{ target: CrossFieldTarget.Destination, ...newAttachId },
|
|
3383
|
+
countToProcess,
|
|
3384
|
+
this.fieldId,
|
|
3385
|
+
);
|
|
3386
|
+
} else {
|
|
3387
|
+
this.table.removedCrossFieldKeys.set(
|
|
3388
|
+
{ target: CrossFieldTarget.Source, ...baseDetachId },
|
|
3389
|
+
countToProcess,
|
|
3390
|
+
true,
|
|
3391
|
+
);
|
|
3392
|
+
|
|
3393
|
+
this.table.removedCrossFieldKeys.set(
|
|
3394
|
+
{ target: CrossFieldTarget.Destination, ...newAttachId },
|
|
3395
|
+
countToProcess,
|
|
3396
|
+
true,
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
if (countToProcess < count) {
|
|
3401
|
+
this.composeAttachDetach(
|
|
3402
|
+
offsetChangeAtomId(baseDetachId, countToProcess),
|
|
3403
|
+
offsetChangeAtomId(newAttachId, countToProcess),
|
|
3404
|
+
count - countToProcess,
|
|
3405
|
+
);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
private invalidateBaseFields(fields: FieldId[]): void {
|
|
3410
|
+
if (this.allowInval) {
|
|
3411
|
+
for (const fieldId of fields) {
|
|
3412
|
+
this.table.pendingCompositions.affectedBaseFields.set(
|
|
3413
|
+
fieldIdKeyFromFieldId(fieldId),
|
|
3414
|
+
true,
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
function makeModularChangeset(props?: {
|
|
3422
|
+
rebaseVersion?: RebaseVersion;
|
|
3423
|
+
fieldChanges?: FieldChangeMap;
|
|
3424
|
+
nodeChanges?: ChangeAtomIdBTree<NodeChangeset>;
|
|
3425
|
+
rootNodes?: RootNodeTable;
|
|
3426
|
+
nodeToParent?: ChangeAtomIdBTree<NodeLocation>;
|
|
3427
|
+
nodeAliases?: ChangeAtomIdBTree<NodeId>;
|
|
3428
|
+
crossFieldKeys?: CrossFieldKeyTable;
|
|
3429
|
+
maxId: number;
|
|
3430
|
+
revisions?: readonly RevisionInfo[];
|
|
3431
|
+
constraintViolationCount?: number;
|
|
3432
|
+
constraintViolationCountOnRevert?: number;
|
|
3433
|
+
noChangeConstraint?: NoChangeConstraint;
|
|
3434
|
+
noChangeConstraintOnRevert?: NoChangeConstraint;
|
|
3435
|
+
builds?: ChangeAtomIdBTree<TreeChunk>;
|
|
3436
|
+
destroys?: ChangeAtomIdBTree<number>;
|
|
3437
|
+
refreshers?: ChangeAtomIdBTree<TreeChunk>;
|
|
3438
|
+
}): ModularChangeset {
|
|
3439
|
+
const p = props ?? { maxId: -1 };
|
|
2631
3440
|
const changeset: Mutable<ModularChangeset> = {
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
3441
|
+
rebaseVersion: p.rebaseVersion ?? 1,
|
|
3442
|
+
fieldChanges: p.fieldChanges ?? new Map(),
|
|
3443
|
+
nodeChanges: p.nodeChanges ?? newTupleBTree(),
|
|
3444
|
+
rootNodes: p.rootNodes ?? newRootTable(),
|
|
3445
|
+
nodeToParent: p.nodeToParent ?? newTupleBTree(),
|
|
3446
|
+
nodeAliases: p.nodeAliases ?? newTupleBTree(),
|
|
3447
|
+
crossFieldKeys: p.crossFieldKeys ?? newCrossFieldRangeTable(),
|
|
2637
3448
|
};
|
|
2638
3449
|
|
|
2639
|
-
if (
|
|
2640
|
-
changeset.revisions =
|
|
3450
|
+
if (p.revisions !== undefined && p.revisions.length > 0) {
|
|
3451
|
+
changeset.revisions = p.revisions;
|
|
2641
3452
|
}
|
|
2642
|
-
if (
|
|
2643
|
-
changeset.maxId = brand(
|
|
3453
|
+
if (p.maxId >= 0) {
|
|
3454
|
+
changeset.maxId = brand(p.maxId);
|
|
2644
3455
|
}
|
|
2645
|
-
if (
|
|
2646
|
-
changeset.constraintViolationCount =
|
|
3456
|
+
if (p.constraintViolationCount !== undefined && p.constraintViolationCount > 0) {
|
|
3457
|
+
changeset.constraintViolationCount = p.constraintViolationCount;
|
|
2647
3458
|
}
|
|
2648
3459
|
if (
|
|
2649
|
-
|
|
2650
|
-
|
|
3460
|
+
p.constraintViolationCountOnRevert !== undefined &&
|
|
3461
|
+
p.constraintViolationCountOnRevert > 0
|
|
2651
3462
|
) {
|
|
2652
|
-
changeset.constraintViolationCountOnRevert =
|
|
3463
|
+
changeset.constraintViolationCountOnRevert = p.constraintViolationCountOnRevert;
|
|
2653
3464
|
}
|
|
2654
|
-
if (
|
|
2655
|
-
changeset.noChangeConstraint =
|
|
3465
|
+
if (p.noChangeConstraint !== undefined) {
|
|
3466
|
+
changeset.noChangeConstraint = p.noChangeConstraint;
|
|
2656
3467
|
}
|
|
2657
|
-
if (
|
|
2658
|
-
changeset.noChangeConstraintOnRevert =
|
|
3468
|
+
if (p.noChangeConstraintOnRevert !== undefined) {
|
|
3469
|
+
changeset.noChangeConstraintOnRevert = p.noChangeConstraintOnRevert;
|
|
2659
3470
|
}
|
|
2660
|
-
if (
|
|
2661
|
-
changeset.builds =
|
|
3471
|
+
if (p.builds !== undefined && p.builds.size > 0) {
|
|
3472
|
+
changeset.builds = p.builds;
|
|
2662
3473
|
}
|
|
2663
|
-
if (
|
|
2664
|
-
changeset.destroys =
|
|
3474
|
+
if (p.destroys !== undefined && p.destroys.size > 0) {
|
|
3475
|
+
changeset.destroys = p.destroys;
|
|
2665
3476
|
}
|
|
2666
|
-
if (
|
|
2667
|
-
changeset.refreshers =
|
|
3477
|
+
if (p.refreshers !== undefined && p.refreshers.size > 0) {
|
|
3478
|
+
changeset.refreshers = p.refreshers;
|
|
2668
3479
|
}
|
|
3480
|
+
|
|
2669
3481
|
return changeset;
|
|
2670
3482
|
}
|
|
2671
3483
|
|
|
@@ -2685,6 +3497,10 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2685
3497
|
this.codecOptions = codecOptions;
|
|
2686
3498
|
}
|
|
2687
3499
|
|
|
3500
|
+
public isInTransaction(): boolean {
|
|
3501
|
+
return this.transactionDepth > 0;
|
|
3502
|
+
}
|
|
3503
|
+
|
|
2688
3504
|
public override enterTransaction(): void {
|
|
2689
3505
|
this.transactionDepth += 1;
|
|
2690
3506
|
if (this.transactionDepth === 1) {
|
|
@@ -2739,7 +3555,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2739
3555
|
* @param revision - the revision of the change
|
|
2740
3556
|
*/
|
|
2741
3557
|
public submitChange(
|
|
2742
|
-
field:
|
|
3558
|
+
field: NormalizedFieldUpPath,
|
|
2743
3559
|
fieldKind: FieldKindIdentifier,
|
|
2744
3560
|
change: FieldChangeset,
|
|
2745
3561
|
revision: RevisionTag,
|
|
@@ -2753,7 +3569,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2753
3569
|
fieldChange: { fieldKind, change },
|
|
2754
3570
|
nodeChanges: newTupleBTree(),
|
|
2755
3571
|
nodeToParent: newTupleBTree(),
|
|
2756
|
-
crossFieldKeys:
|
|
3572
|
+
crossFieldKeys: newCrossFieldRangeTable(),
|
|
3573
|
+
rootNodes: newRootTable(),
|
|
2757
3574
|
idAllocator: this.idAllocator,
|
|
2758
3575
|
localCrossFieldKeys,
|
|
2759
3576
|
revision,
|
|
@@ -2775,6 +3592,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2775
3592
|
? makeModularChangeset({
|
|
2776
3593
|
maxId: this.idAllocator.getMaxId(),
|
|
2777
3594
|
builds: change.builds,
|
|
3595
|
+
rootNodes: renameTableFromRenameDescriptions(change.renames ?? []),
|
|
2778
3596
|
revisions: [{ revision: change.revision }],
|
|
2779
3597
|
})
|
|
2780
3598
|
: buildModularChangesetFromField({
|
|
@@ -2785,7 +3603,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2785
3603
|
},
|
|
2786
3604
|
nodeChanges: newTupleBTree(),
|
|
2787
3605
|
nodeToParent: newTupleBTree(),
|
|
2788
|
-
crossFieldKeys:
|
|
3606
|
+
crossFieldKeys: newCrossFieldRangeTable(),
|
|
3607
|
+
rootNodes: newRootTable(),
|
|
2789
3608
|
idAllocator: this.idAllocator,
|
|
2790
3609
|
localCrossFieldKeys: getChangeHandler(
|
|
2791
3610
|
this.fieldKinds,
|
|
@@ -2812,7 +3631,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2812
3631
|
return brand(this.idAllocator.allocate(count));
|
|
2813
3632
|
}
|
|
2814
3633
|
|
|
2815
|
-
public addNodeExistsConstraint(path:
|
|
3634
|
+
public addNodeExistsConstraint(path: NormalizedUpPath, revision: RevisionTag): void {
|
|
2816
3635
|
const nodeChange: NodeChangeset = {
|
|
2817
3636
|
nodeExistsConstraint: { violated: false },
|
|
2818
3637
|
};
|
|
@@ -2824,7 +3643,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2824
3643
|
nodeChange,
|
|
2825
3644
|
nodeChanges: newTupleBTree(),
|
|
2826
3645
|
nodeToParent: newTupleBTree(),
|
|
2827
|
-
crossFieldKeys:
|
|
3646
|
+
crossFieldKeys: newCrossFieldRangeTable(),
|
|
3647
|
+
rootNodes: newRootTable(),
|
|
2828
3648
|
idAllocator: this.idAllocator,
|
|
2829
3649
|
revision,
|
|
2830
3650
|
}),
|
|
@@ -2833,7 +3653,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2833
3653
|
);
|
|
2834
3654
|
}
|
|
2835
3655
|
|
|
2836
|
-
public addNodeExistsConstraintOnRevert(path:
|
|
3656
|
+
public addNodeExistsConstraintOnRevert(path: NormalizedUpPath, revision: RevisionTag): void {
|
|
2837
3657
|
const nodeChange: NodeChangeset = {
|
|
2838
3658
|
nodeExistsConstraintOnRevert: { violated: false },
|
|
2839
3659
|
};
|
|
@@ -2845,7 +3665,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2845
3665
|
nodeChange,
|
|
2846
3666
|
nodeChanges: newTupleBTree(),
|
|
2847
3667
|
nodeToParent: newTupleBTree(),
|
|
2848
|
-
crossFieldKeys:
|
|
3668
|
+
crossFieldKeys: newCrossFieldRangeTable(),
|
|
3669
|
+
rootNodes: newRootTable(),
|
|
2849
3670
|
idAllocator: this.idAllocator,
|
|
2850
3671
|
revision,
|
|
2851
3672
|
}),
|
|
@@ -2885,12 +3706,13 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
2885
3706
|
}
|
|
2886
3707
|
}
|
|
2887
3708
|
|
|
2888
|
-
function buildModularChangesetFromField(props: {
|
|
2889
|
-
path:
|
|
3709
|
+
export function buildModularChangesetFromField(props: {
|
|
3710
|
+
path: NormalizedFieldUpPath;
|
|
2890
3711
|
fieldChange: FieldChange;
|
|
2891
3712
|
nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
|
|
2892
|
-
nodeToParent: ChangeAtomIdBTree<
|
|
3713
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
2893
3714
|
crossFieldKeys: CrossFieldKeyTable;
|
|
3715
|
+
rootNodes: RootNodeTable;
|
|
2894
3716
|
localCrossFieldKeys?: CrossFieldKeyRange[];
|
|
2895
3717
|
revision: RevisionTag;
|
|
2896
3718
|
idAllocator?: IdAllocator;
|
|
@@ -2902,6 +3724,7 @@ function buildModularChangesetFromField(props: {
|
|
|
2902
3724
|
nodeChanges,
|
|
2903
3725
|
nodeToParent,
|
|
2904
3726
|
crossFieldKeys,
|
|
3727
|
+
rootNodes,
|
|
2905
3728
|
idAllocator = idAllocatorFromMaxId(),
|
|
2906
3729
|
localCrossFieldKeys = [],
|
|
2907
3730
|
childId,
|
|
@@ -2910,14 +3733,17 @@ function buildModularChangesetFromField(props: {
|
|
|
2910
3733
|
const fieldChanges: FieldChangeMap = new Map([[path.field, fieldChange]]);
|
|
2911
3734
|
|
|
2912
3735
|
if (path.parent === undefined) {
|
|
3736
|
+
const field = { nodeId: undefined, field: path.field };
|
|
2913
3737
|
for (const { key, count } of localCrossFieldKeys) {
|
|
2914
|
-
crossFieldKeys.set(key, count,
|
|
3738
|
+
crossFieldKeys.set(key, count, field);
|
|
2915
3739
|
}
|
|
2916
3740
|
|
|
2917
3741
|
if (childId !== undefined) {
|
|
2918
3742
|
setInChangeAtomIdMap(nodeToParent, childId, {
|
|
2919
|
-
|
|
2920
|
-
|
|
3743
|
+
field: {
|
|
3744
|
+
nodeId: undefined,
|
|
3745
|
+
field: path.field,
|
|
3746
|
+
},
|
|
2921
3747
|
});
|
|
2922
3748
|
}
|
|
2923
3749
|
|
|
@@ -2926,6 +3752,7 @@ function buildModularChangesetFromField(props: {
|
|
|
2926
3752
|
nodeChanges,
|
|
2927
3753
|
nodeToParent,
|
|
2928
3754
|
crossFieldKeys,
|
|
3755
|
+
rootNodes,
|
|
2929
3756
|
maxId: idAllocator.getMaxId(),
|
|
2930
3757
|
revisions: [{ revision }],
|
|
2931
3758
|
});
|
|
@@ -2936,6 +3763,7 @@ function buildModularChangesetFromField(props: {
|
|
|
2936
3763
|
};
|
|
2937
3764
|
|
|
2938
3765
|
const parentId: NodeId = { localId: brand(idAllocator.allocate()), revision };
|
|
3766
|
+
const fieldId = { nodeId: parentId, field: path.field };
|
|
2939
3767
|
|
|
2940
3768
|
for (const { key, count } of localCrossFieldKeys) {
|
|
2941
3769
|
crossFieldKeys.set(key, count, { nodeId: parentId, field: path.field });
|
|
@@ -2943,8 +3771,7 @@ function buildModularChangesetFromField(props: {
|
|
|
2943
3771
|
|
|
2944
3772
|
if (childId !== undefined) {
|
|
2945
3773
|
setInChangeAtomIdMap(nodeToParent, childId, {
|
|
2946
|
-
|
|
2947
|
-
field: path.field,
|
|
3774
|
+
field: fieldId,
|
|
2948
3775
|
});
|
|
2949
3776
|
}
|
|
2950
3777
|
|
|
@@ -2954,6 +3781,7 @@ function buildModularChangesetFromField(props: {
|
|
|
2954
3781
|
nodeChanges,
|
|
2955
3782
|
nodeToParent,
|
|
2956
3783
|
crossFieldKeys,
|
|
3784
|
+
rootNodes,
|
|
2957
3785
|
idAllocator,
|
|
2958
3786
|
revision,
|
|
2959
3787
|
nodeId: parentId,
|
|
@@ -2961,11 +3789,12 @@ function buildModularChangesetFromField(props: {
|
|
|
2961
3789
|
}
|
|
2962
3790
|
|
|
2963
3791
|
function buildModularChangesetFromNode(props: {
|
|
2964
|
-
path:
|
|
3792
|
+
path: NormalizedUpPath;
|
|
2965
3793
|
nodeChange: NodeChangeset;
|
|
2966
3794
|
nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
|
|
2967
|
-
nodeToParent: ChangeAtomIdBTree<
|
|
3795
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
2968
3796
|
crossFieldKeys: CrossFieldKeyTable;
|
|
3797
|
+
rootNodes: RootNodeTable;
|
|
2969
3798
|
idAllocator: IdAllocator;
|
|
2970
3799
|
revision: RevisionTag;
|
|
2971
3800
|
nodeId?: NodeId;
|
|
@@ -2979,27 +3808,43 @@ function buildModularChangesetFromNode(props: {
|
|
|
2979
3808
|
nodeId = { localId: brand(idAllocator.allocate()), revision },
|
|
2980
3809
|
} = props;
|
|
2981
3810
|
setInChangeAtomIdMap(nodeChanges, nodeId, nodeChange);
|
|
2982
|
-
const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
|
|
2983
|
-
[path.parentIndex, nodeId],
|
|
2984
|
-
]);
|
|
2985
3811
|
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3812
|
+
if (isDetachedUpPathRoot(path)) {
|
|
3813
|
+
props.rootNodes.nodeChanges.set(
|
|
3814
|
+
[path.detachedNodeId.major, brand(path.detachedNodeId.minor)],
|
|
3815
|
+
nodeId,
|
|
3816
|
+
);
|
|
3817
|
+
return makeModularChangeset({
|
|
3818
|
+
rootNodes: props.rootNodes,
|
|
3819
|
+
nodeChanges: props.nodeChanges,
|
|
3820
|
+
nodeToParent: props.nodeToParent,
|
|
3821
|
+
crossFieldKeys: props.crossFieldKeys,
|
|
3822
|
+
maxId: props.idAllocator.getMaxId(),
|
|
3823
|
+
revisions: [{ revision: props.revision }],
|
|
3824
|
+
});
|
|
3825
|
+
} else {
|
|
3826
|
+
const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
|
|
3827
|
+
[path.parentIndex, nodeId],
|
|
3828
|
+
]);
|
|
2990
3829
|
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3830
|
+
const fieldChange: FieldChange = {
|
|
3831
|
+
fieldKind: genericFieldKind.identifier,
|
|
3832
|
+
change: fieldChangeset,
|
|
3833
|
+
};
|
|
3834
|
+
|
|
3835
|
+
return buildModularChangesetFromField({
|
|
3836
|
+
...props,
|
|
3837
|
+
path: { parent: path.parent, field: path.parentField },
|
|
3838
|
+
fieldChange,
|
|
3839
|
+
localCrossFieldKeys: [],
|
|
3840
|
+
childId: nodeId,
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
2998
3843
|
}
|
|
2999
3844
|
|
|
3000
3845
|
export interface FieldEditDescription {
|
|
3001
3846
|
type: "field";
|
|
3002
|
-
field:
|
|
3847
|
+
field: NormalizedFieldUpPath;
|
|
3003
3848
|
fieldKind: FieldKindIdentifier;
|
|
3004
3849
|
change: FieldChangeset;
|
|
3005
3850
|
revision: RevisionTag;
|
|
@@ -3009,6 +3854,23 @@ export interface GlobalEditDescription {
|
|
|
3009
3854
|
type: "global";
|
|
3010
3855
|
revision: RevisionTag;
|
|
3011
3856
|
builds?: ChangeAtomIdBTree<TreeChunk>;
|
|
3857
|
+
renames?: RenameDescription[];
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
export interface RenameDescription {
|
|
3861
|
+
count: number;
|
|
3862
|
+
oldId: ChangeAtomId;
|
|
3863
|
+
newId: ChangeAtomId;
|
|
3864
|
+
detachLocation: FieldId | undefined;
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
function renameTableFromRenameDescriptions(renames: RenameDescription[]): RootNodeTable {
|
|
3868
|
+
const table = newRootTable();
|
|
3869
|
+
for (const rename of renames) {
|
|
3870
|
+
addNodeRename(table, rename.oldId, rename.newId, rename.count, rename.detachLocation);
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
return table;
|
|
3012
3874
|
}
|
|
3013
3875
|
|
|
3014
3876
|
export type EditDescription = FieldEditDescription | GlobalEditDescription;
|
|
@@ -3032,20 +3894,6 @@ function getRevInfoFromTaggedChanges(changes: TaggedChange<ModularChangeset>[]):
|
|
|
3032
3894
|
}
|
|
3033
3895
|
}
|
|
3034
3896
|
|
|
3035
|
-
const rolledBackRevisions: RevisionTag[] = [];
|
|
3036
|
-
for (const info of revInfos) {
|
|
3037
|
-
if (info.rollbackOf !== undefined) {
|
|
3038
|
-
rolledBackRevisions.push(info.rollbackOf);
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
|
|
3042
|
-
rolledBackRevisions.reverse();
|
|
3043
|
-
for (const revision of rolledBackRevisions) {
|
|
3044
|
-
if (!revisions.has(revision)) {
|
|
3045
|
-
revInfos.push({ revision });
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
|
|
3049
3897
|
return { maxId: brand(maxId), revInfos };
|
|
3050
3898
|
}
|
|
3051
3899
|
|
|
@@ -3065,25 +3913,28 @@ function revisionInfoFromTaggedChange(
|
|
|
3065
3913
|
return revInfos;
|
|
3066
3914
|
}
|
|
3067
3915
|
|
|
3068
|
-
function fieldChangeFromId(
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3916
|
+
function fieldChangeFromId(change: ModularChangeset, id: FieldId): FieldChange {
|
|
3917
|
+
const fieldId = normalizeFieldId(id, change.nodeAliases);
|
|
3918
|
+
const fieldMap = fieldMapFromNodeId(
|
|
3919
|
+
change.fieldChanges,
|
|
3920
|
+
change.nodeChanges,
|
|
3921
|
+
change.nodeAliases,
|
|
3922
|
+
fieldId.nodeId,
|
|
3923
|
+
);
|
|
3074
3924
|
return fieldMap.get(id.field) ?? fail(0xb25 /* No field exists for the given ID */);
|
|
3075
3925
|
}
|
|
3076
3926
|
|
|
3077
3927
|
function fieldMapFromNodeId(
|
|
3078
3928
|
rootFieldMap: FieldChangeMap,
|
|
3079
3929
|
nodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
3930
|
+
aliases: ChangeAtomIdBTree<NodeId>,
|
|
3080
3931
|
nodeId: NodeId | undefined,
|
|
3081
3932
|
): FieldChangeMap {
|
|
3082
3933
|
if (nodeId === undefined) {
|
|
3083
3934
|
return rootFieldMap;
|
|
3084
3935
|
}
|
|
3085
3936
|
|
|
3086
|
-
const node = nodeChangeFromId(nodes, nodeId);
|
|
3937
|
+
const node = nodeChangeFromId(nodes, aliases, nodeId);
|
|
3087
3938
|
assert(node.fieldChanges !== undefined, 0x9c9 /* Expected node to have field changes */);
|
|
3088
3939
|
return node.fieldChanges;
|
|
3089
3940
|
}
|
|
@@ -3100,8 +3951,13 @@ function rebasedNodeIdFromBaseNodeId(table: RebaseTable, baseId: NodeId): NodeId
|
|
|
3100
3951
|
return getFromChangeAtomIdMap(table.baseToRebasedNodeId, baseId) ?? baseId;
|
|
3101
3952
|
}
|
|
3102
3953
|
|
|
3103
|
-
function nodeChangeFromId(
|
|
3104
|
-
|
|
3954
|
+
function nodeChangeFromId(
|
|
3955
|
+
nodes: ChangeAtomIdBTree<NodeChangeset>,
|
|
3956
|
+
aliases: ChangeAtomIdBTree<NodeId>,
|
|
3957
|
+
id: NodeId,
|
|
3958
|
+
): NodeChangeset {
|
|
3959
|
+
const normalizedId = normalizeNodeId(id, aliases);
|
|
3960
|
+
const node = getFromChangeAtomIdMap(nodes, normalizedId);
|
|
3105
3961
|
assert(node !== undefined, 0x9ca /* Unknown node ID */);
|
|
3106
3962
|
return node;
|
|
3107
3963
|
}
|
|
@@ -3111,6 +3967,10 @@ function fieldIdFromFieldIdKey([revision, localId, field]: FieldIdKey): FieldId
|
|
|
3111
3967
|
return { nodeId, field };
|
|
3112
3968
|
}
|
|
3113
3969
|
|
|
3970
|
+
function fieldIdKeyFromFieldId(fieldId: FieldId): FieldIdKey {
|
|
3971
|
+
return [fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field];
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3114
3974
|
function cloneNodeChangeset(nodeChangeset: NodeChangeset): NodeChangeset {
|
|
3115
3975
|
if (nodeChangeset.fieldChanges !== undefined) {
|
|
3116
3976
|
return { ...nodeChangeset, fieldChanges: new Map(nodeChangeset.fieldChanges) };
|
|
@@ -3119,6 +3979,15 @@ function cloneNodeChangeset(nodeChangeset: NodeChangeset): NodeChangeset {
|
|
|
3119
3979
|
return { ...nodeChangeset };
|
|
3120
3980
|
}
|
|
3121
3981
|
|
|
3982
|
+
function replaceNodeLocationRevision(
|
|
3983
|
+
location: NodeLocation,
|
|
3984
|
+
replacer: RevisionReplacer,
|
|
3985
|
+
): NodeLocation {
|
|
3986
|
+
return location.field === undefined
|
|
3987
|
+
? { root: replacer.getUpdatedAtomId(location.root) }
|
|
3988
|
+
: { field: replaceFieldIdRevision(location.field, replacer) };
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3122
3991
|
function replaceFieldIdRevision(fieldId: FieldId, replacer: RevisionReplacer): FieldId {
|
|
3123
3992
|
if (fieldId.nodeId === undefined) {
|
|
3124
3993
|
return fieldId;
|
|
@@ -3130,10 +3999,16 @@ function replaceFieldIdRevision(fieldId: FieldId, replacer: RevisionReplacer): F
|
|
|
3130
3999
|
};
|
|
3131
4000
|
}
|
|
3132
4001
|
|
|
3133
|
-
export function
|
|
3134
|
-
const
|
|
3135
|
-
|
|
3136
|
-
|
|
4002
|
+
export function getNodeParent(changeset: ModularChangeset, nodeId: NodeId): NodeLocation {
|
|
4003
|
+
const normalizedNodeId = normalizeNodeId(nodeId, changeset.nodeAliases);
|
|
4004
|
+
const location = getFromChangeAtomIdMap(changeset.nodeToParent, normalizedNodeId);
|
|
4005
|
+
assert(location !== undefined, 0x9cb /* Parent field should be defined */);
|
|
4006
|
+
|
|
4007
|
+
if (location.field !== undefined) {
|
|
4008
|
+
return { field: normalizeFieldId(location.field, changeset.nodeAliases) };
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
return location;
|
|
3137
4012
|
}
|
|
3138
4013
|
|
|
3139
4014
|
function getFieldsForCrossFieldKey(
|
|
@@ -3146,6 +4021,30 @@ function getFieldsForCrossFieldKey(
|
|
|
3146
4021
|
.map(({ value: fieldId }) => normalizeFieldId(fieldId, changeset.nodeAliases));
|
|
3147
4022
|
}
|
|
3148
4023
|
|
|
4024
|
+
function getFirstFieldForCrossFieldKey(
|
|
4025
|
+
changeset: ModularChangeset,
|
|
4026
|
+
key: CrossFieldKey,
|
|
4027
|
+
count: number,
|
|
4028
|
+
): RangeQueryResult<FieldId | undefined> {
|
|
4029
|
+
const result = changeset.crossFieldKeys.getFirst(key, count);
|
|
4030
|
+
if (result.value === undefined) {
|
|
4031
|
+
return result;
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
return { ...result, value: normalizeFieldId(result.value, changeset.nodeAliases) };
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
function normalizeNodeLocation(
|
|
4038
|
+
location: NodeLocation,
|
|
4039
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
4040
|
+
): NodeLocation {
|
|
4041
|
+
if (location.field !== undefined) {
|
|
4042
|
+
return { field: normalizeFieldId(location.field, nodeAliases) };
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
return location;
|
|
4046
|
+
}
|
|
4047
|
+
|
|
3149
4048
|
// This is only exported for use in test utilities.
|
|
3150
4049
|
export function normalizeFieldId(
|
|
3151
4050
|
fieldId: FieldId,
|
|
@@ -3159,8 +4058,12 @@ export function normalizeFieldId(
|
|
|
3159
4058
|
/**
|
|
3160
4059
|
* @returns The canonical form of nodeId, according to nodeAliases
|
|
3161
4060
|
*/
|
|
3162
|
-
function normalizeNodeId(
|
|
4061
|
+
export function normalizeNodeId(
|
|
4062
|
+
nodeId: NodeId,
|
|
4063
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
4064
|
+
): NodeId {
|
|
3163
4065
|
let currentId = nodeId;
|
|
4066
|
+
let cycleProbeId: NodeId | undefined = nodeId;
|
|
3164
4067
|
|
|
3165
4068
|
// eslint-disable-next-line no-constant-condition
|
|
3166
4069
|
while (true) {
|
|
@@ -3170,6 +4073,16 @@ function normalizeNodeId(nodeId: NodeId, nodeAliases: ChangeAtomIdBTree<NodeId>)
|
|
|
3170
4073
|
}
|
|
3171
4074
|
|
|
3172
4075
|
currentId = dealiased;
|
|
4076
|
+
|
|
4077
|
+
if (cycleProbeId !== undefined) {
|
|
4078
|
+
cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4081
|
+
if (cycleProbeId !== undefined) {
|
|
4082
|
+
cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
assert(!areEqualChangeAtomIdOpts(cycleProbeId, currentId), "Alias cycle detected");
|
|
3173
4086
|
}
|
|
3174
4087
|
}
|
|
3175
4088
|
|
|
@@ -3180,7 +4093,8 @@ function hasConflicts(change: ModularChangeset): boolean {
|
|
|
3180
4093
|
interface ModularChangesetContent {
|
|
3181
4094
|
fieldChanges: FieldChangeMap;
|
|
3182
4095
|
nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
|
|
3183
|
-
nodeToParent: ChangeAtomIdBTree<
|
|
4096
|
+
nodeToParent: ChangeAtomIdBTree<NodeLocation>;
|
|
4097
|
+
rootNodes: RootNodeTable;
|
|
3184
4098
|
nodeAliases: ChangeAtomIdBTree<NodeId>;
|
|
3185
4099
|
crossFieldKeys: CrossFieldKeyTable;
|
|
3186
4100
|
}
|
|
@@ -3188,3 +4102,927 @@ interface ModularChangesetContent {
|
|
|
3188
4102
|
function areEqualFieldIds(a: FieldId, b: FieldId): boolean {
|
|
3189
4103
|
return areEqualChangeAtomIdOpts(a.nodeId, b.nodeId) && a.field === b.field;
|
|
3190
4104
|
}
|
|
4105
|
+
|
|
4106
|
+
function firstAttachIdFromDetachId(
|
|
4107
|
+
roots: RootNodeTable,
|
|
4108
|
+
detachId: ChangeAtomId,
|
|
4109
|
+
count: number,
|
|
4110
|
+
): RangeQueryResult<ChangeAtomId> {
|
|
4111
|
+
const result = roots.oldToNewId.getFirst(detachId, count);
|
|
4112
|
+
return { ...result, value: result.value ?? detachId };
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
function firstDetachIdFromAttachId(
|
|
4116
|
+
roots: RootNodeTable,
|
|
4117
|
+
attachId: ChangeAtomId,
|
|
4118
|
+
count: number,
|
|
4119
|
+
): RangeQueryEntry<ChangeAtomId, ChangeAtomId> {
|
|
4120
|
+
const result = roots.newToOldId.getFirst(attachId, count);
|
|
4121
|
+
return { ...result, start: attachId, value: result.value ?? attachId };
|
|
4122
|
+
}
|
|
4123
|
+
|
|
4124
|
+
function rebaseCrossFieldKeys(
|
|
4125
|
+
sourceTable: CrossFieldKeyTable,
|
|
4126
|
+
movedDetaches: ChangeAtomIdRangeMap<boolean>,
|
|
4127
|
+
newDetachLocations: ChangeAtomIdRangeMap<FieldId>,
|
|
4128
|
+
): CrossFieldKeyTable {
|
|
4129
|
+
const rebasedTable = sourceTable.clone();
|
|
4130
|
+
for (const entry of movedDetaches.entries()) {
|
|
4131
|
+
rebasedTable.delete({ ...entry.start, target: CrossFieldTarget.Source }, entry.length);
|
|
4132
|
+
}
|
|
4133
|
+
|
|
4134
|
+
for (const entry of newDetachLocations.entries()) {
|
|
4135
|
+
rebasedTable.set(
|
|
4136
|
+
{ ...entry.start, target: CrossFieldTarget.Source },
|
|
4137
|
+
entry.length,
|
|
4138
|
+
entry.value,
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4141
|
+
|
|
4142
|
+
return rebasedTable;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
export function newRootTable(): RootNodeTable {
|
|
4146
|
+
return {
|
|
4147
|
+
newToOldId: newChangeAtomIdTransform(),
|
|
4148
|
+
oldToNewId: newChangeAtomIdTransform(),
|
|
4149
|
+
nodeChanges: newTupleBTree(),
|
|
4150
|
+
detachLocations: newChangeAtomIdRangeMap(),
|
|
4151
|
+
outputDetachLocations: newChangeAtomIdRangeMap(),
|
|
4152
|
+
};
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
function rebaseRoots(
|
|
4156
|
+
change: ModularChangeset,
|
|
4157
|
+
base: ModularChangeset,
|
|
4158
|
+
affectedBaseFields: TupleBTree<FieldIdKey, boolean>,
|
|
4159
|
+
nodesToRebase: [newChangeset: NodeId, baseChangeset: NodeId][],
|
|
4160
|
+
rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
4161
|
+
rebaseVersion: RebaseVersion,
|
|
4162
|
+
): RootNodeTable {
|
|
4163
|
+
const rebasedRoots = newRootTable();
|
|
4164
|
+
for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
|
|
4165
|
+
rebaseRename(change.rootNodes, rebasedRoots, renameEntry, base, affectedBaseFields);
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4168
|
+
for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
|
|
4169
|
+
const changes = base.rootNodes.nodeChanges.get(detachIdKey);
|
|
4170
|
+
if (changes !== undefined) {
|
|
4171
|
+
nodesToRebase.push([nodeId, changes]);
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
const detachId = makeChangeAtomId(detachIdKey[1], detachIdKey[0]);
|
|
4175
|
+
const attachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
|
|
4176
|
+
const baseAttachEntry = base.crossFieldKeys.getFirst(
|
|
4177
|
+
{ target: CrossFieldTarget.Destination, ...attachId },
|
|
4178
|
+
1,
|
|
4179
|
+
);
|
|
4180
|
+
if (baseAttachEntry.value === undefined) {
|
|
4181
|
+
const renamedDetachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
|
|
4182
|
+
const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(
|
|
4183
|
+
renamedDetachId,
|
|
4184
|
+
1,
|
|
4185
|
+
).value;
|
|
4186
|
+
|
|
4187
|
+
if (baseOutputDetachLocation !== undefined) {
|
|
4188
|
+
affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
const detachLocation =
|
|
4192
|
+
baseOutputDetachLocation ??
|
|
4193
|
+
change.rootNodes.detachLocations.getFirst(detachId, 1).value;
|
|
4194
|
+
|
|
4195
|
+
// Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
|
|
4196
|
+
// We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
|
|
4197
|
+
assignRootChange(
|
|
4198
|
+
rebasedRoots,
|
|
4199
|
+
rebasedNodeToParent,
|
|
4200
|
+
renamedDetachId,
|
|
4201
|
+
nodeId,
|
|
4202
|
+
detachLocation,
|
|
4203
|
+
rebaseVersion,
|
|
4204
|
+
);
|
|
4205
|
+
} else {
|
|
4206
|
+
affectedBaseFields.set(fieldIdKeyFromFieldId(baseAttachEntry.value), true);
|
|
4207
|
+
rebasedNodeToParent.delete(detachIdKey);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
for (const entry of change.rootNodes.outputDetachLocations.entries()) {
|
|
4212
|
+
rebasedRoots.outputDetachLocations.set(entry.start, entry.length, entry.value);
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
return rebasedRoots;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
function rebaseRename(
|
|
4219
|
+
newRoots: RootNodeTable,
|
|
4220
|
+
rebasedRoots: RootNodeTable,
|
|
4221
|
+
renameEntry: RangeQueryEntry<ChangeAtomId, ChangeAtomId>,
|
|
4222
|
+
base: ModularChangeset,
|
|
4223
|
+
affectedBaseFields: TupleBTree<FieldIdKey, boolean>,
|
|
4224
|
+
): void {
|
|
4225
|
+
let count = renameEntry.length;
|
|
4226
|
+
const baseRenameEntry = firstAttachIdFromDetachId(base.rootNodes, renameEntry.start, count);
|
|
4227
|
+
count = baseRenameEntry.length;
|
|
4228
|
+
|
|
4229
|
+
const baseAttachEntry = base.crossFieldKeys.getFirst(
|
|
4230
|
+
{
|
|
4231
|
+
...baseRenameEntry.value,
|
|
4232
|
+
target: CrossFieldTarget.Destination,
|
|
4233
|
+
},
|
|
4234
|
+
count,
|
|
4235
|
+
);
|
|
4236
|
+
|
|
4237
|
+
count = baseAttachEntry.length;
|
|
4238
|
+
|
|
4239
|
+
if (baseAttachEntry.value === undefined) {
|
|
4240
|
+
const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(
|
|
4241
|
+
baseRenameEntry.value,
|
|
4242
|
+
1,
|
|
4243
|
+
).value;
|
|
4244
|
+
|
|
4245
|
+
if (baseOutputDetachLocation !== undefined) {
|
|
4246
|
+
affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
const detachEntry = newRoots.detachLocations.getFirst(renameEntry.start, count);
|
|
4250
|
+
count = detachEntry.length;
|
|
4251
|
+
|
|
4252
|
+
const detachLocation = baseOutputDetachLocation ?? detachEntry.value;
|
|
4253
|
+
|
|
4254
|
+
// Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
|
|
4255
|
+
// We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
|
|
4256
|
+
addNodeRename(
|
|
4257
|
+
rebasedRoots,
|
|
4258
|
+
baseRenameEntry.value,
|
|
4259
|
+
renameEntry.value,
|
|
4260
|
+
count,
|
|
4261
|
+
detachLocation,
|
|
4262
|
+
);
|
|
4263
|
+
} else {
|
|
4264
|
+
// This rename represents an intention to detach these nodes.
|
|
4265
|
+
// The rebased change should have a detach in the field where the base change attaches the nodes,
|
|
4266
|
+
// so we need to ensure that field is processed.
|
|
4267
|
+
affectedBaseFields.set(
|
|
4268
|
+
fieldIdKeyFromFieldId(normalizeFieldId(baseAttachEntry.value, base.nodeAliases)),
|
|
4269
|
+
true,
|
|
4270
|
+
);
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
const countRemaining = renameEntry.length - count;
|
|
4274
|
+
if (countRemaining > 0) {
|
|
4275
|
+
rebaseRename(
|
|
4276
|
+
newRoots,
|
|
4277
|
+
rebasedRoots,
|
|
4278
|
+
{
|
|
4279
|
+
start: offsetChangeAtomId(renameEntry.start, count),
|
|
4280
|
+
value: offsetChangeAtomId(renameEntry.value, count),
|
|
4281
|
+
length: countRemaining,
|
|
4282
|
+
},
|
|
4283
|
+
base,
|
|
4284
|
+
affectedBaseFields,
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
/**
|
|
4290
|
+
* For each root detach location, replaces any node ID from the base changeset
|
|
4291
|
+
* with the corresponding ID in the new changeset.
|
|
4292
|
+
*/
|
|
4293
|
+
function fixupRebasedDetachLocations(table: RebaseTable): void {
|
|
4294
|
+
for (const {
|
|
4295
|
+
start,
|
|
4296
|
+
length,
|
|
4297
|
+
value: detachLocation,
|
|
4298
|
+
} of table.rebasedRootNodes.detachLocations.entries()) {
|
|
4299
|
+
const normalizedDetachLocation = normalizeFieldId(
|
|
4300
|
+
detachLocation,
|
|
4301
|
+
table.baseChange.nodeAliases,
|
|
4302
|
+
);
|
|
4303
|
+
|
|
4304
|
+
if (normalizedDetachLocation.nodeId !== undefined) {
|
|
4305
|
+
const rebasedNodeId = getFromChangeAtomIdMap(
|
|
4306
|
+
table.baseToRebasedNodeId,
|
|
4307
|
+
normalizedDetachLocation.nodeId,
|
|
4308
|
+
);
|
|
4309
|
+
|
|
4310
|
+
if (rebasedNodeId !== undefined) {
|
|
4311
|
+
table.rebasedRootNodes.detachLocations.set(start, length, {
|
|
4312
|
+
...normalizedDetachLocation,
|
|
4313
|
+
nodeId: rebasedNodeId,
|
|
4314
|
+
});
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
function addNodesToCompose(table: ComposeTable, id1: NodeId, id2: NodeId): void {
|
|
4321
|
+
const normalizedId1 = normalizeNodeId(id1, table.baseChange.nodeAliases);
|
|
4322
|
+
const normalizedId2 = normalizeNodeId(id2, table.newChange.nodeAliases);
|
|
4323
|
+
if (getFromChangeAtomIdMap(table.newToBaseNodeId, normalizedId2) === undefined) {
|
|
4324
|
+
setInChangeAtomIdMap(table.newToBaseNodeId, normalizedId2, normalizedId1);
|
|
4325
|
+
table.pendingCompositions.nodeIdsToCompose.push([normalizedId1, normalizedId2]);
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
function composeRevInfos(
|
|
4330
|
+
revisions1: readonly RevisionInfo[] | undefined,
|
|
4331
|
+
revisions2: readonly RevisionInfo[] | undefined,
|
|
4332
|
+
): readonly RevisionInfo[] {
|
|
4333
|
+
if (
|
|
4334
|
+
revisions1?.length === 1 &&
|
|
4335
|
+
revisions2?.length === 1 &&
|
|
4336
|
+
revisions1[0]?.revision === revisions2[0]?.revision
|
|
4337
|
+
) {
|
|
4338
|
+
// This is a special case where we are composing two changesets from the same transaction.
|
|
4339
|
+
// We return one of the input arrays to avoid duplicating revision entries.
|
|
4340
|
+
return revisions1;
|
|
4341
|
+
}
|
|
4342
|
+
const result: RevisionInfo[] = [...(revisions1 ?? []), ...(revisions2 ?? [])];
|
|
4343
|
+
return result;
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4346
|
+
function composeCrossFieldKeyTables(
|
|
4347
|
+
table1: CrossFieldKeyTable,
|
|
4348
|
+
table2: CrossFieldKeyTable,
|
|
4349
|
+
movedCrossFieldKeys: CrossFieldKeyTable,
|
|
4350
|
+
removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
|
|
4351
|
+
): CrossFieldKeyTable {
|
|
4352
|
+
const composedTable = RangeMap.union(table1, table2);
|
|
4353
|
+
for (const entry of movedCrossFieldKeys.entries()) {
|
|
4354
|
+
composedTable.set(entry.start, entry.length, entry.value);
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
for (const entry of removedCrossFieldKeys.entries()) {
|
|
4358
|
+
composedTable.delete(entry.start, entry.length);
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
return composedTable;
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
function composeRootTables(
|
|
4365
|
+
change1: ModularChangeset,
|
|
4366
|
+
change2: ModularChangeset,
|
|
4367
|
+
composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
|
|
4368
|
+
movedCrossFieldKeys: CrossFieldKeyTable,
|
|
4369
|
+
removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
|
|
4370
|
+
pendingCompositions: PendingCompositions,
|
|
4371
|
+
): RootNodeTable {
|
|
4372
|
+
const composedTable = cloneRootTable(change1.rootNodes);
|
|
4373
|
+
|
|
4374
|
+
for (const renameEntry of change2.rootNodes.oldToNewId.entries()) {
|
|
4375
|
+
composeRename(
|
|
4376
|
+
change1,
|
|
4377
|
+
change2,
|
|
4378
|
+
composedTable,
|
|
4379
|
+
renameEntry.start,
|
|
4380
|
+
renameEntry.value,
|
|
4381
|
+
renameEntry.length,
|
|
4382
|
+
movedCrossFieldKeys,
|
|
4383
|
+
removedCrossFieldKeys,
|
|
4384
|
+
pendingCompositions,
|
|
4385
|
+
);
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4388
|
+
for (const [[revision2, id2], nodeId2] of change2.rootNodes.nodeChanges.entries()) {
|
|
4389
|
+
const detachId2 = { revision: revision2, localId: id2 };
|
|
4390
|
+
const detachId1 = firstDetachIdFromAttachId(change1.rootNodes, detachId2, 1).value;
|
|
4391
|
+
const nodeId1 = getFromChangeAtomIdMap(change1.rootNodes.nodeChanges, detachId1);
|
|
4392
|
+
|
|
4393
|
+
if (nodeId1 === undefined) {
|
|
4394
|
+
const fieldId = getFieldsForCrossFieldKey(
|
|
4395
|
+
change1,
|
|
4396
|
+
{ ...detachId1, target: CrossFieldTarget.Source },
|
|
4397
|
+
1,
|
|
4398
|
+
)[0];
|
|
4399
|
+
|
|
4400
|
+
if (fieldId === undefined) {
|
|
4401
|
+
assignRootChange(
|
|
4402
|
+
composedTable,
|
|
4403
|
+
composedNodeToParent,
|
|
4404
|
+
detachId1,
|
|
4405
|
+
nodeId2,
|
|
4406
|
+
change1.rootNodes.detachLocations.getFirst(detachId1, 1).value ??
|
|
4407
|
+
change2.rootNodes.detachLocations.getFirst(detachId2, 1).value,
|
|
4408
|
+
Math.max(change1.rebaseVersion, change2.rebaseVersion) as RebaseVersion,
|
|
4409
|
+
);
|
|
4410
|
+
} else {
|
|
4411
|
+
// In this case, this node is attached in the input context of change1,
|
|
4412
|
+
// and is represented in detachFieldId.
|
|
4413
|
+
pendingCompositions.affectedBaseFields.set(
|
|
4414
|
+
[fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field],
|
|
4415
|
+
true,
|
|
4416
|
+
);
|
|
4417
|
+
}
|
|
4418
|
+
} else {
|
|
4419
|
+
pendingCompositions.nodeIdsToCompose.push([nodeId1, nodeId2]);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
for (const outputDetachEntry of change1.rootNodes.outputDetachLocations.entries()) {
|
|
4424
|
+
composeOutputDetachLocation(
|
|
4425
|
+
outputDetachEntry.start,
|
|
4426
|
+
outputDetachEntry.length,
|
|
4427
|
+
outputDetachEntry.value,
|
|
4428
|
+
change2,
|
|
4429
|
+
composedTable,
|
|
4430
|
+
);
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
for (const entry of change2.rootNodes.outputDetachLocations.entries()) {
|
|
4434
|
+
composedTable.outputDetachLocations.set(entry.start, entry.length, entry.value);
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
return composedTable;
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4440
|
+
function composeOutputDetachLocation(
|
|
4441
|
+
outputDetachId1: ChangeAtomId,
|
|
4442
|
+
count: number,
|
|
4443
|
+
detachLocation: FieldId,
|
|
4444
|
+
change2: ModularChangeset,
|
|
4445
|
+
composedTable: RootNodeTable,
|
|
4446
|
+
): void {
|
|
4447
|
+
let countToProcess = count;
|
|
4448
|
+
const renameEntry = firstAttachIdFromDetachId(
|
|
4449
|
+
change2.rootNodes,
|
|
4450
|
+
outputDetachId1,
|
|
4451
|
+
countToProcess,
|
|
4452
|
+
);
|
|
4453
|
+
countToProcess = renameEntry.length;
|
|
4454
|
+
|
|
4455
|
+
const attachEntry = getFirstAttachField(
|
|
4456
|
+
change2.crossFieldKeys,
|
|
4457
|
+
renameEntry.value,
|
|
4458
|
+
countToProcess,
|
|
4459
|
+
);
|
|
4460
|
+
countToProcess = attachEntry.length;
|
|
4461
|
+
|
|
4462
|
+
composedTable.outputDetachLocations.delete(outputDetachId1, countToProcess);
|
|
4463
|
+
|
|
4464
|
+
if (attachEntry.value === undefined) {
|
|
4465
|
+
// We update the key for the detach location to the renamed ID of the root in the composed output context.
|
|
4466
|
+
composedTable.outputDetachLocations.set(renameEntry.value, countToProcess, detachLocation);
|
|
4467
|
+
} else {
|
|
4468
|
+
// These nodes are attached by `change2` and thus attached in the composed output context,
|
|
4469
|
+
// so there should be no output detach location.
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
const countRemaining = count - countToProcess;
|
|
4473
|
+
if (countRemaining > 0) {
|
|
4474
|
+
composeOutputDetachLocation(
|
|
4475
|
+
offsetChangeAtomId(outputDetachId1, countToProcess),
|
|
4476
|
+
countRemaining,
|
|
4477
|
+
detachLocation,
|
|
4478
|
+
change2,
|
|
4479
|
+
composedTable,
|
|
4480
|
+
);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
function composeRename(
|
|
4485
|
+
change1: ModularChangeset,
|
|
4486
|
+
change2: ModularChangeset,
|
|
4487
|
+
mergedTable: RootNodeTable,
|
|
4488
|
+
oldId: ChangeAtomId,
|
|
4489
|
+
newId: ChangeAtomId,
|
|
4490
|
+
count: number,
|
|
4491
|
+
movedCrossFieldKeys: CrossFieldKeyTable,
|
|
4492
|
+
removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
|
|
4493
|
+
pendingCompositions: PendingCompositions,
|
|
4494
|
+
): void {
|
|
4495
|
+
let countToProcess = count;
|
|
4496
|
+
const detachEntry = getFirstDetachField(change1.crossFieldKeys, oldId, countToProcess);
|
|
4497
|
+
countToProcess = detachEntry.length;
|
|
4498
|
+
|
|
4499
|
+
if (detachEntry.value === undefined) {
|
|
4500
|
+
// `change1` may also have a rename to `renameEntry.value`, in which case it must refer to a different node.
|
|
4501
|
+
// That node must have been attached by `change1` and detached by `change2`.
|
|
4502
|
+
// The final rename for that node will be created in `composeAttachDetach`.
|
|
4503
|
+
// We delete any such rename for now to avoid colliding with the rename currently being processed.
|
|
4504
|
+
deleteNodeRenameTo(mergedTable, newId, countToProcess);
|
|
4505
|
+
|
|
4506
|
+
// The nodes were detached before `change`, so we append this rename.
|
|
4507
|
+
appendNodeRename(
|
|
4508
|
+
mergedTable,
|
|
4509
|
+
oldId,
|
|
4510
|
+
newId,
|
|
4511
|
+
countToProcess,
|
|
4512
|
+
change1.rootNodes,
|
|
4513
|
+
change2.rootNodes.detachLocations.getFirst(oldId, countToProcess).value,
|
|
4514
|
+
);
|
|
4515
|
+
} else {
|
|
4516
|
+
// `change1` detached these nodes,
|
|
4517
|
+
// so we invalidate the detach location so that the detach's ID can be replaced with the new ID.
|
|
4518
|
+
pendingCompositions.affectedBaseFields.set(fieldIdKeyFromFieldId(detachEntry.value), true);
|
|
4519
|
+
|
|
4520
|
+
if (!areEqualChangeAtomIds(oldId, newId)) {
|
|
4521
|
+
// `change1`'s detach will be replaced by `change2`'s detach, so we update the cross-field keys.
|
|
4522
|
+
removedCrossFieldKeys.set(
|
|
4523
|
+
{ ...oldId, target: CrossFieldTarget.Source },
|
|
4524
|
+
countToProcess,
|
|
4525
|
+
true,
|
|
4526
|
+
);
|
|
4527
|
+
}
|
|
4528
|
+
|
|
4529
|
+
movedCrossFieldKeys.set(
|
|
4530
|
+
{ ...newId, target: CrossFieldTarget.Source },
|
|
4531
|
+
countToProcess,
|
|
4532
|
+
detachEntry.value,
|
|
4533
|
+
);
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
if (countToProcess < count) {
|
|
4537
|
+
composeRename(
|
|
4538
|
+
change1,
|
|
4539
|
+
change2,
|
|
4540
|
+
mergedTable,
|
|
4541
|
+
offsetChangeAtomId(oldId, countToProcess),
|
|
4542
|
+
offsetChangeAtomId(newId, countToProcess),
|
|
4543
|
+
count - countToProcess,
|
|
4544
|
+
movedCrossFieldKeys,
|
|
4545
|
+
removedCrossFieldKeys,
|
|
4546
|
+
pendingCompositions,
|
|
4547
|
+
);
|
|
4548
|
+
}
|
|
4549
|
+
}
|
|
4550
|
+
|
|
4551
|
+
export function cloneRootTable(table: RootNodeTable): RootNodeTable {
|
|
4552
|
+
return {
|
|
4553
|
+
oldToNewId: table.oldToNewId.clone(),
|
|
4554
|
+
newToOldId: table.newToOldId.clone(),
|
|
4555
|
+
nodeChanges: brand(table.nodeChanges.clone()),
|
|
4556
|
+
detachLocations: table.detachLocations.clone(),
|
|
4557
|
+
outputDetachLocations: table.outputDetachLocations.clone(),
|
|
4558
|
+
};
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4561
|
+
function invertRootTable(change: ModularChangeset, isRollback: boolean): RootNodeTable {
|
|
4562
|
+
const invertedRoots: RootNodeTable = newRootTable();
|
|
4563
|
+
for (const [[revision, localId], nodeId] of change.rootNodes.nodeChanges.entries()) {
|
|
4564
|
+
const detachId: ChangeAtomId = { revision, localId };
|
|
4565
|
+
const renamedId = firstAttachIdFromDetachId(change.rootNodes, detachId, 1).value;
|
|
4566
|
+
|
|
4567
|
+
// This checks whether `change` attaches this node.
|
|
4568
|
+
// If it does, the node is not detached in the input context of the inverse, and so should not be included in the root table.
|
|
4569
|
+
if (
|
|
4570
|
+
change.crossFieldKeys.getFirst({ ...renamedId, target: CrossFieldTarget.Destination }, 1)
|
|
4571
|
+
.value === undefined
|
|
4572
|
+
) {
|
|
4573
|
+
assignRootChange(
|
|
4574
|
+
invertedRoots,
|
|
4575
|
+
undefined,
|
|
4576
|
+
renamedId,
|
|
4577
|
+
nodeId,
|
|
4578
|
+
change.rootNodes.detachLocations.getFirst(detachId, 1).value,
|
|
4579
|
+
change.rebaseVersion,
|
|
4580
|
+
);
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
if (isRollback) {
|
|
4585
|
+
// We only invert renames of nodes which are not attached or detached by this changeset.
|
|
4586
|
+
// When we invert an attach we will create a detach which incorporates the rename.
|
|
4587
|
+
for (const {
|
|
4588
|
+
start: oldId,
|
|
4589
|
+
value: newId,
|
|
4590
|
+
length,
|
|
4591
|
+
} of change.rootNodes.oldToNewId.entries()) {
|
|
4592
|
+
invertRename(change, invertedRoots, oldId, newId, length);
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
return invertedRoots;
|
|
4597
|
+
}
|
|
4598
|
+
|
|
4599
|
+
function invertRename(
|
|
4600
|
+
change: ModularChangeset,
|
|
4601
|
+
invertedRoots: RootNodeTable,
|
|
4602
|
+
oldId: ChangeAtomId,
|
|
4603
|
+
newId: ChangeAtomId,
|
|
4604
|
+
length: number,
|
|
4605
|
+
): void {
|
|
4606
|
+
for (const detachEntry of doesChangeDetachNodes(change.crossFieldKeys, newId, length)) {
|
|
4607
|
+
assert(
|
|
4608
|
+
!detachEntry.value,
|
|
4609
|
+
"A changeset should not have a rename and detach for the same node.",
|
|
4610
|
+
);
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4613
|
+
let countProcessed = length;
|
|
4614
|
+
const attachEntry = getFirstAttachField(change.crossFieldKeys, newId, countProcessed);
|
|
4615
|
+
countProcessed = attachEntry.length;
|
|
4616
|
+
if (attachEntry.value === undefined) {
|
|
4617
|
+
const outputDetachEntry = change.rootNodes.outputDetachLocations.getFirst(
|
|
4618
|
+
newId,
|
|
4619
|
+
countProcessed,
|
|
4620
|
+
);
|
|
4621
|
+
countProcessed = outputDetachEntry.length;
|
|
4622
|
+
|
|
4623
|
+
const inputDetachEntry = change.rootNodes.detachLocations.getFirst(oldId, countProcessed);
|
|
4624
|
+
countProcessed = inputDetachEntry.length;
|
|
4625
|
+
|
|
4626
|
+
addNodeRename(
|
|
4627
|
+
invertedRoots,
|
|
4628
|
+
newId,
|
|
4629
|
+
oldId,
|
|
4630
|
+
countProcessed,
|
|
4631
|
+
outputDetachEntry.value ?? inputDetachEntry.value,
|
|
4632
|
+
);
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
if (countProcessed < length) {
|
|
4636
|
+
invertRename(
|
|
4637
|
+
change,
|
|
4638
|
+
invertedRoots,
|
|
4639
|
+
offsetChangeAtomId(oldId, countProcessed),
|
|
4640
|
+
offsetChangeAtomId(newId, countProcessed),
|
|
4641
|
+
length - countProcessed,
|
|
4642
|
+
);
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4646
|
+
function doesChangeAttachNodes(
|
|
4647
|
+
table: CrossFieldKeyTable,
|
|
4648
|
+
id: ChangeAtomId,
|
|
4649
|
+
count: number,
|
|
4650
|
+
): RangeQueryResultFragment<boolean>[] {
|
|
4651
|
+
return table
|
|
4652
|
+
.getAll2({ ...id, target: CrossFieldTarget.Destination }, count)
|
|
4653
|
+
.map((entry) => ({ ...entry, value: entry.value !== undefined }));
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4656
|
+
function doesChangeDetachNodes(
|
|
4657
|
+
table: CrossFieldKeyTable,
|
|
4658
|
+
id: ChangeAtomId,
|
|
4659
|
+
count: number,
|
|
4660
|
+
): RangeQueryResultFragment<boolean>[] {
|
|
4661
|
+
return table
|
|
4662
|
+
.getAll2({ ...id, target: CrossFieldTarget.Source }, count)
|
|
4663
|
+
.map((entry) => ({ ...entry, value: entry.value !== undefined }));
|
|
4664
|
+
}
|
|
4665
|
+
|
|
4666
|
+
export function getFirstDetachField(
|
|
4667
|
+
table: CrossFieldKeyTable,
|
|
4668
|
+
id: ChangeAtomId,
|
|
4669
|
+
count: number,
|
|
4670
|
+
): RangeQueryResult<FieldId | undefined> {
|
|
4671
|
+
return table.getFirst({ target: CrossFieldTarget.Source, ...id }, count);
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4674
|
+
export function getFirstAttachField(
|
|
4675
|
+
table: CrossFieldKeyTable,
|
|
4676
|
+
id: ChangeAtomId,
|
|
4677
|
+
count: number,
|
|
4678
|
+
): RangeQueryResult<FieldId | undefined> {
|
|
4679
|
+
return table.getFirst({ target: CrossFieldTarget.Destination, ...id }, count);
|
|
4680
|
+
}
|
|
4681
|
+
|
|
4682
|
+
export function addNodeRename(
|
|
4683
|
+
table: RootNodeTable,
|
|
4684
|
+
oldId: ChangeAtomId,
|
|
4685
|
+
newId: ChangeAtomId,
|
|
4686
|
+
count: number,
|
|
4687
|
+
detachLocation: FieldId | undefined,
|
|
4688
|
+
): void {
|
|
4689
|
+
if (areEqualChangeAtomIds(oldId, newId)) {
|
|
4690
|
+
return;
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4693
|
+
for (const entry of table.oldToNewId.getAll2(oldId, count)) {
|
|
4694
|
+
assert(
|
|
4695
|
+
entry.value === undefined ||
|
|
4696
|
+
areEqualChangeAtomIds(entry.value, offsetChangeAtomId(newId, entry.offset)),
|
|
4697
|
+
"Rename collision detected",
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4701
|
+
for (const entry of table.newToOldId.getAll2(newId, count)) {
|
|
4702
|
+
assert(
|
|
4703
|
+
entry.value === undefined ||
|
|
4704
|
+
areEqualChangeAtomIds(entry.value, offsetChangeAtomId(oldId, entry.offset)),
|
|
4705
|
+
"Rename collision detected",
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
|
|
4709
|
+
table.oldToNewId.set(oldId, count, newId);
|
|
4710
|
+
table.newToOldId.set(newId, count, oldId);
|
|
4711
|
+
|
|
4712
|
+
if (detachLocation !== undefined) {
|
|
4713
|
+
table.detachLocations.set(oldId, count, detachLocation);
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
/**
|
|
4718
|
+
* Deletes any renames from `id`.
|
|
4719
|
+
*/
|
|
4720
|
+
function deleteNodeRenameFrom(roots: RootNodeTable, id: ChangeAtomId, count: number): void {
|
|
4721
|
+
for (const entry of roots.oldToNewId.getAll(id, count)) {
|
|
4722
|
+
deleteNodeRenameEntry(roots, entry.start, entry.value, entry.length);
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
|
|
4726
|
+
/**
|
|
4727
|
+
* Deletes any renames to `id`.
|
|
4728
|
+
*/
|
|
4729
|
+
function deleteNodeRenameTo(roots: RootNodeTable, id: ChangeAtomId, count: number): void {
|
|
4730
|
+
for (const entry of roots.newToOldId.getAll(id, count)) {
|
|
4731
|
+
deleteNodeRenameEntry(roots, entry.value, entry.start, entry.length);
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
function appendNodeRename(
|
|
4736
|
+
composedTable: RootNodeTable,
|
|
4737
|
+
oldId: ChangeAtomId,
|
|
4738
|
+
newId: ChangeAtomId,
|
|
4739
|
+
count: number,
|
|
4740
|
+
change1Table: RootNodeTable,
|
|
4741
|
+
detachLocation: FieldId | undefined,
|
|
4742
|
+
): void {
|
|
4743
|
+
let countToProcess = count;
|
|
4744
|
+
const rename1Entry = change1Table.newToOldId.getFirst(oldId, countToProcess);
|
|
4745
|
+
countToProcess = rename1Entry.length;
|
|
4746
|
+
|
|
4747
|
+
if (rename1Entry.value !== undefined) {
|
|
4748
|
+
deleteNodeRenameFrom(composedTable, rename1Entry.value, countToProcess);
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4751
|
+
addNodeRename(
|
|
4752
|
+
composedTable,
|
|
4753
|
+
rename1Entry.value ?? oldId,
|
|
4754
|
+
newId,
|
|
4755
|
+
countToProcess,
|
|
4756
|
+
detachLocation,
|
|
4757
|
+
);
|
|
4758
|
+
|
|
4759
|
+
tryRemoveDetachLocation(composedTable, newId, countToProcess);
|
|
4760
|
+
|
|
4761
|
+
if (countToProcess < count) {
|
|
4762
|
+
const countRemaining = count - countToProcess;
|
|
4763
|
+
appendNodeRename(
|
|
4764
|
+
composedTable,
|
|
4765
|
+
offsetChangeAtomId(oldId, countToProcess),
|
|
4766
|
+
offsetChangeAtomId(newId, countToProcess),
|
|
4767
|
+
countRemaining,
|
|
4768
|
+
change1Table,
|
|
4769
|
+
detachLocation,
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
|
|
4774
|
+
function tryRemoveDetachLocation(
|
|
4775
|
+
roots: RootNodeTable,
|
|
4776
|
+
rootId: ChangeAtomId,
|
|
4777
|
+
count: number,
|
|
4778
|
+
): void {
|
|
4779
|
+
let countProcessed = count;
|
|
4780
|
+
const renameEntry = roots.oldToNewId.getFirst(rootId, countProcessed);
|
|
4781
|
+
countProcessed = renameEntry.length;
|
|
4782
|
+
|
|
4783
|
+
const outputDetachEntry = roots.outputDetachLocations.getFirst(rootId, countProcessed);
|
|
4784
|
+
countProcessed = outputDetachEntry.length;
|
|
4785
|
+
|
|
4786
|
+
const nodeChangeEntry = rangeQueryChangeAtomIdMap(roots.nodeChanges, rootId, countProcessed);
|
|
4787
|
+
countProcessed = nodeChangeEntry.length;
|
|
4788
|
+
|
|
4789
|
+
if (
|
|
4790
|
+
nodeChangeEntry.value === undefined &&
|
|
4791
|
+
renameEntry.value === undefined &&
|
|
4792
|
+
outputDetachEntry.value === undefined
|
|
4793
|
+
) {
|
|
4794
|
+
roots.detachLocations.delete(rootId, countProcessed);
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4797
|
+
const countRemaining = count - countProcessed;
|
|
4798
|
+
if (countRemaining > 0) {
|
|
4799
|
+
tryRemoveDetachLocation(roots, offsetChangeAtomId(rootId, countProcessed), countRemaining);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
/**
|
|
4804
|
+
* Deletes the entry renaming the ID range of length `count` from `oldId` to `newId`.
|
|
4805
|
+
* This function assumes that such an entry exists.
|
|
4806
|
+
*/
|
|
4807
|
+
function deleteNodeRenameEntry(
|
|
4808
|
+
roots: RootNodeTable,
|
|
4809
|
+
oldId: ChangeAtomId,
|
|
4810
|
+
newId: ChangeAtomId,
|
|
4811
|
+
count: number,
|
|
4812
|
+
): void {
|
|
4813
|
+
roots.oldToNewId.delete(oldId, count);
|
|
4814
|
+
roots.newToOldId.delete(newId, count);
|
|
4815
|
+
}
|
|
4816
|
+
|
|
4817
|
+
function replaceRootTableRevision(
|
|
4818
|
+
table: RootNodeTable,
|
|
4819
|
+
replacer: RevisionReplacer,
|
|
4820
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
4821
|
+
): RootNodeTable {
|
|
4822
|
+
const oldToNewId = table.oldToNewId.mapEntries(
|
|
4823
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4824
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4825
|
+
);
|
|
4826
|
+
|
|
4827
|
+
const newToOldId = table.newToOldId.mapEntries(
|
|
4828
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4829
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4830
|
+
);
|
|
4831
|
+
|
|
4832
|
+
const nodeChanges: ChangeAtomIdBTree<NodeId> = replaceIdMapRevisions(
|
|
4833
|
+
table.nodeChanges,
|
|
4834
|
+
replacer,
|
|
4835
|
+
(nodeId) => replacer.getUpdatedAtomId(normalizeNodeId(nodeId, nodeAliases)),
|
|
4836
|
+
);
|
|
4837
|
+
|
|
4838
|
+
const detachLocations = table.detachLocations.mapEntries(
|
|
4839
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4840
|
+
(fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer),
|
|
4841
|
+
);
|
|
4842
|
+
|
|
4843
|
+
const outputDetachLocations = table.outputDetachLocations.mapEntries(
|
|
4844
|
+
(id) => replacer.getUpdatedAtomId(id),
|
|
4845
|
+
(fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer),
|
|
4846
|
+
);
|
|
4847
|
+
|
|
4848
|
+
return { oldToNewId, newToOldId, nodeChanges, detachLocations, outputDetachLocations };
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
function newDetachedEntryMap(): ChangeAtomIdRangeMap<DetachedNodeEntry> {
|
|
4852
|
+
return new RangeMap(offsetChangeAtomId, subtractChangeAtomIds, offsetDetachedNodeEntry);
|
|
4853
|
+
}
|
|
4854
|
+
|
|
4855
|
+
function offsetDetachedNodeEntry(entry: DetachedNodeEntry, count: number): DetachedNodeEntry {
|
|
4856
|
+
assert(
|
|
4857
|
+
count <= 1 || entry.nodeChange === undefined,
|
|
4858
|
+
"Cannot split an entry with a node change",
|
|
4859
|
+
);
|
|
4860
|
+
|
|
4861
|
+
return entry.detachId === undefined
|
|
4862
|
+
? entry
|
|
4863
|
+
: { ...entry, detachId: offsetChangeAtomId(entry.detachId, count) };
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
function getFieldsWithRootMoves(
|
|
4867
|
+
roots: RootNodeTable,
|
|
4868
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
4869
|
+
): TupleBTree<FieldIdKey, boolean> {
|
|
4870
|
+
const fields: TupleBTree<FieldIdKey, boolean> = newTupleBTree();
|
|
4871
|
+
for (const { start: rootId, value: fieldId, length } of roots.detachLocations.entries()) {
|
|
4872
|
+
let isRootMoved = false;
|
|
4873
|
+
for (const renameEntry of roots.oldToNewId.getAll2(rootId, length)) {
|
|
4874
|
+
if (renameEntry.value !== undefined) {
|
|
4875
|
+
isRootMoved = true;
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
|
|
4879
|
+
for (const outputDetachEntry of roots.outputDetachLocations.getAll2(rootId, length)) {
|
|
4880
|
+
if (outputDetachEntry.value !== undefined) {
|
|
4881
|
+
isRootMoved = true;
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
if (isRootMoved) {
|
|
4886
|
+
fields.set(fieldIdKeyFromFieldId(normalizeFieldId(fieldId, nodeAliases)), true);
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
return fields;
|
|
4891
|
+
}
|
|
4892
|
+
|
|
4893
|
+
function getFieldToRootChanges(
|
|
4894
|
+
roots: RootNodeTable,
|
|
4895
|
+
nodeAliases: ChangeAtomIdBTree<NodeId>,
|
|
4896
|
+
): TupleBTree<FieldIdKey, ChangeAtomId[]> {
|
|
4897
|
+
const fields: TupleBTree<FieldIdKey, ChangeAtomId[]> = newTupleBTree();
|
|
4898
|
+
for (const rootIdKey of roots.nodeChanges.keys()) {
|
|
4899
|
+
const rootId: ChangeAtomId = { revision: rootIdKey[0], localId: rootIdKey[1] };
|
|
4900
|
+
const detachLocation = roots.detachLocations.getFirst(rootId, 1).value;
|
|
4901
|
+
if (detachLocation !== undefined) {
|
|
4902
|
+
const fieldIdKey = fieldIdKeyFromFieldId(normalizeFieldId(detachLocation, nodeAliases));
|
|
4903
|
+
let rootsInField = fields.get(fieldIdKey);
|
|
4904
|
+
if (rootsInField === undefined) {
|
|
4905
|
+
rootsInField = [];
|
|
4906
|
+
fields.set(fieldIdKey, rootsInField);
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
rootsInField.push(rootId);
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
|
|
4913
|
+
return fields;
|
|
4914
|
+
}
|
|
4915
|
+
|
|
4916
|
+
function muteRootChanges(roots: RootNodeTable): RootNodeTable {
|
|
4917
|
+
return {
|
|
4918
|
+
oldToNewId: newChangeAtomIdTransform(),
|
|
4919
|
+
newToOldId: newChangeAtomIdTransform(),
|
|
4920
|
+
nodeChanges: brand(roots.nodeChanges.clone()),
|
|
4921
|
+
detachLocations: roots.detachLocations.clone(),
|
|
4922
|
+
outputDetachLocations: newChangeAtomIdRangeMap(),
|
|
4923
|
+
};
|
|
4924
|
+
}
|
|
4925
|
+
|
|
4926
|
+
export function validateChangeset(
|
|
4927
|
+
change: ModularChangeset,
|
|
4928
|
+
fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
|
|
4929
|
+
): void {
|
|
4930
|
+
const unreachableNodes: ChangeAtomIdBTree<NodeLocation> = brand(change.nodeToParent.clone());
|
|
4931
|
+
|
|
4932
|
+
const unreachableCFKs = change.crossFieldKeys.clone();
|
|
4933
|
+
|
|
4934
|
+
validateFieldChanges(
|
|
4935
|
+
fieldKinds,
|
|
4936
|
+
change,
|
|
4937
|
+
change.fieldChanges,
|
|
4938
|
+
undefined,
|
|
4939
|
+
unreachableNodes,
|
|
4940
|
+
unreachableCFKs,
|
|
4941
|
+
);
|
|
4942
|
+
|
|
4943
|
+
for (const [[revision, localId], node] of change.nodeChanges.entries()) {
|
|
4944
|
+
if (node.fieldChanges === undefined) {
|
|
4945
|
+
continue;
|
|
4946
|
+
}
|
|
4947
|
+
|
|
4948
|
+
const nodeId = normalizeNodeId({ revision, localId }, change.nodeAliases);
|
|
4949
|
+
validateFieldChanges(
|
|
4950
|
+
fieldKinds,
|
|
4951
|
+
change,
|
|
4952
|
+
node.fieldChanges,
|
|
4953
|
+
nodeId,
|
|
4954
|
+
unreachableNodes,
|
|
4955
|
+
unreachableCFKs,
|
|
4956
|
+
);
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
|
|
4960
|
+
const detachId: ChangeAtomId = { revision: detachIdKey[0], localId: detachIdKey[1] };
|
|
4961
|
+
const location = getNodeParent(change, nodeId);
|
|
4962
|
+
assert(areEqualChangeAtomIdOpts(location.root, detachId), "Inconsistent node location");
|
|
4963
|
+
|
|
4964
|
+
const normalizedNodeId = normalizeNodeId(nodeId, change.nodeAliases);
|
|
4965
|
+
unreachableNodes.delete([normalizedNodeId.revision, normalizedNodeId.localId]);
|
|
4966
|
+
|
|
4967
|
+
const fieldChanges = nodeChangeFromId(
|
|
4968
|
+
change.nodeChanges,
|
|
4969
|
+
change.nodeAliases,
|
|
4970
|
+
nodeId,
|
|
4971
|
+
).fieldChanges;
|
|
4972
|
+
|
|
4973
|
+
if (fieldChanges !== undefined) {
|
|
4974
|
+
validateFieldChanges(
|
|
4975
|
+
fieldKinds,
|
|
4976
|
+
change,
|
|
4977
|
+
fieldChanges,
|
|
4978
|
+
normalizedNodeId,
|
|
4979
|
+
unreachableNodes,
|
|
4980
|
+
unreachableCFKs,
|
|
4981
|
+
);
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
|
|
4985
|
+
assert(unreachableNodes.size === 0, "Unreachable nodes found");
|
|
4986
|
+
assert(unreachableCFKs.entries().length === 0, "Unreachable cross-field keys found");
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
/**
|
|
4990
|
+
* Asserts that each node has a correct entry in `change.nodeToParent`,
|
|
4991
|
+
* and each cross field key has a correct entry in `change.crossFieldKeys`.
|
|
4992
|
+
* @returns the number of children found.
|
|
4993
|
+
*/
|
|
4994
|
+
function validateFieldChanges(
|
|
4995
|
+
fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
|
|
4996
|
+
change: ModularChangeset,
|
|
4997
|
+
fieldChanges: FieldChangeMap,
|
|
4998
|
+
nodeParent: NodeId | undefined,
|
|
4999
|
+
unreachableNodes: ChangeAtomIdBTree<NodeLocation>,
|
|
5000
|
+
unreachableCFKs: CrossFieldRangeTable<FieldId>,
|
|
5001
|
+
): void {
|
|
5002
|
+
for (const [field, fieldChange] of fieldChanges.entries()) {
|
|
5003
|
+
const fieldId = { nodeId: nodeParent, field };
|
|
5004
|
+
const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
|
|
5005
|
+
for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
|
|
5006
|
+
const parentFieldId = getNodeParent(change, child);
|
|
5007
|
+
assert(
|
|
5008
|
+
parentFieldId.field !== undefined && areEqualFieldIds(parentFieldId.field, fieldId),
|
|
5009
|
+
0xa4e /* Inconsistent node parentage */,
|
|
5010
|
+
);
|
|
5011
|
+
|
|
5012
|
+
unreachableNodes.delete([child.revision, child.localId]);
|
|
5013
|
+
}
|
|
5014
|
+
|
|
5015
|
+
for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
|
|
5016
|
+
const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
|
|
5017
|
+
assert(fields.length > 0, "Unregistered cross-field key");
|
|
5018
|
+
for (const fieldFromLookup of fields) {
|
|
5019
|
+
assert(
|
|
5020
|
+
areEqualFieldIds(fieldFromLookup, fieldId),
|
|
5021
|
+
0xa4f /* Inconsistent cross field keys */,
|
|
5022
|
+
);
|
|
5023
|
+
}
|
|
5024
|
+
|
|
5025
|
+
unreachableCFKs.delete(keyRange.key, keyRange.count);
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
}
|