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