@fluidframework/tree 2.1.0-276985 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +7 -0
- package/.vscode/Tree.code-workspace +9 -2
- package/CHANGELOG.md +38 -0
- package/README.md +55 -12
- package/api-report/tree.alpha.api.md +2 -1
- package/api-report/tree.beta.api.md +2 -1
- package/api-report/tree.public.api.md +2 -1
- package/beta.d.ts +1 -1
- package/dist/beta.d.ts +1 -1
- package/dist/core/forest/editableForest.d.ts +6 -3
- package/dist/core/forest/editableForest.d.ts.map +1 -1
- package/dist/core/forest/editableForest.js +16 -4
- package/dist/core/forest/editableForest.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/index.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 +3 -1
- package/dist/core/rebase/index.js.map +1 -1
- package/dist/core/rebase/types.d.ts +2 -0
- package/dist/core/rebase/types.d.ts.map +1 -1
- package/dist/core/rebase/types.js +9 -1
- package/dist/core/rebase/types.js.map +1 -1
- package/dist/core/tree/anchorSet.d.ts +1 -0
- package/dist/core/tree/anchorSet.d.ts.map +1 -1
- package/dist/core/tree/anchorSet.js +13 -0
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/core/tree/detachedFieldIndex.d.ts +48 -11
- package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndex.js +144 -20
- package/dist/core/tree/detachedFieldIndex.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.js +13 -4
- package/dist/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- 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 -1
- package/dist/core/tree/index.d.ts.map +1 -1
- package/dist/core/tree/index.js.map +1 -1
- package/dist/core/tree/visitDelta.d.ts +3 -1
- package/dist/core/tree/visitDelta.d.ts.map +1 -1
- package/dist/core/tree/visitDelta.js +31 -15
- package/dist/core/tree/visitDelta.js.map +1 -1
- package/dist/core/tree/visitorUtils.d.ts +3 -3
- package/dist/core/tree/visitorUtils.d.ts.map +1 -1
- package/dist/core/tree/visitorUtils.js +4 -4
- package/dist/core/tree/visitorUtils.js.map +1 -1
- package/dist/events/events.d.ts +4 -1
- package/dist/events/events.d.ts.map +1 -1
- package/dist/events/events.js.map +1 -1
- package/dist/feature-libraries/default-schema/defaultEditBuilder.js +1 -1
- 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 +1 -0
- package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
- package/dist/feature-libraries/editableTreeBinder.js +1 -1
- package/dist/feature-libraries/editableTreeBinder.js.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -10
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js +0 -72
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -51
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/flex-tree/index.d.ts +3 -2
- package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/index.js +5 -1
- package/dist/feature-libraries/flex-tree/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts +1 -6
- package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +11 -32
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts +1 -5
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +0 -30
- package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +3 -3
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +6 -3
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts +11 -0
- package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts +96 -0
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -0
- package/dist/feature-libraries/modular-schema/discrepancies.js +264 -0
- package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -0
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +9 -2
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
- package/dist/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/genericFieldKind.js +3 -0
- package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts +2 -1
- package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/index.js +3 -1
- package/dist/feature-libraries/modular-schema/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +42 -26
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +51 -2
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +827 -245
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFormat.js +2 -0
- package/dist/feature-libraries/modular-schema/modularChangeFormat.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +44 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/dist/feature-libraries/node-key/index.d.ts +0 -1
- package/dist/feature-libraries/node-key/index.d.ts.map +1 -1
- package/dist/feature-libraries/node-key/index.js +1 -3
- package/dist/feature-libraries/node-key/index.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +5 -4
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
- package/dist/feature-libraries/optional-field/optionalField.js +1 -0
- package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
- package/dist/feature-libraries/sequence-field/index.d.ts +1 -1
- package/dist/feature-libraries/sequence-field/index.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/index.js +1 -2
- package/dist/feature-libraries/sequence-field/index.js.map +1 -1
- package/dist/feature-libraries/sequence-field/invert.js +1 -1
- package/dist/feature-libraries/sequence-field/invert.js.map +1 -1
- package/dist/feature-libraries/sequence-field/rebase.js +6 -1
- package/dist/feature-libraries/sequence-field/rebase.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 -0
- package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
- package/dist/feature-libraries/sequence-field/utils.d.ts +2 -17
- package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
- package/dist/feature-libraries/sequence-field/utils.js +31 -39
- package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
- package/dist/feature-libraries/typed-schema/typedTreeSchema.d.ts +1 -0
- package/dist/feature-libraries/typed-schema/typedTreeSchema.d.ts.map +1 -1
- package/dist/feature-libraries/typed-schema/typedTreeSchema.js +2 -0
- package/dist/feature-libraries/typed-schema/typedTreeSchema.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/shared-tree/schematizingTreeView.d.ts +4 -2
- package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/dist/shared-tree/schematizingTreeView.js +240 -184
- package/dist/shared-tree/schematizingTreeView.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +5 -1
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +157 -90
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/dist/shared-tree/treeApi.js +1 -1
- package/dist/shared-tree/treeApi.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +10 -1
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +47 -3
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree/treeView.d.ts.map +1 -1
- package/dist/shared-tree/treeView.js +7 -3
- package/dist/shared-tree/treeView.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +6 -0
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js +3 -0
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts +8 -6
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +271 -209
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/arrayNode.d.ts +4 -0
- package/dist/simple-tree/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/arrayNode.js +36 -19
- package/dist/simple-tree/arrayNode.js.map +1 -1
- package/dist/simple-tree/index.d.ts +3 -3
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +2 -1
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/leafNodeSchema.d.ts +22 -1
- package/dist/simple-tree/leafNodeSchema.d.ts.map +1 -1
- package/dist/simple-tree/leafNodeSchema.js +2 -1
- package/dist/simple-tree/leafNodeSchema.js.map +1 -1
- package/dist/simple-tree/mapNode.d.ts.map +1 -1
- package/dist/simple-tree/mapNode.js.map +1 -1
- package/dist/simple-tree/objectNode.d.ts.map +1 -1
- package/dist/simple-tree/objectNode.js +2 -1
- package/dist/simple-tree/objectNode.js.map +1 -1
- package/dist/simple-tree/proxies.d.ts.map +1 -1
- package/dist/simple-tree/proxies.js +9 -25
- package/dist/simple-tree/proxies.js.map +1 -1
- package/dist/simple-tree/proxyBinding.d.ts +4 -0
- package/dist/simple-tree/proxyBinding.d.ts.map +1 -1
- package/dist/simple-tree/proxyBinding.js +23 -1
- package/dist/simple-tree/proxyBinding.js.map +1 -1
- package/dist/simple-tree/schemaFactory.d.ts +16 -1
- package/dist/simple-tree/schemaFactory.d.ts.map +1 -1
- package/dist/simple-tree/schemaFactory.js +32 -4
- package/dist/simple-tree/schemaFactory.js.map +1 -1
- package/dist/simple-tree/schemaTypes.d.ts +36 -1
- package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
- package/dist/simple-tree/schemaTypes.js.map +1 -1
- package/dist/simple-tree/toFlexSchema.d.ts +2 -2
- package/dist/simple-tree/toFlexSchema.d.ts.map +1 -1
- package/dist/simple-tree/toFlexSchema.js +3 -2
- package/dist/simple-tree/toFlexSchema.js.map +1 -1
- package/dist/simple-tree/tree.d.ts +4 -1
- package/dist/simple-tree/tree.d.ts.map +1 -1
- package/dist/simple-tree/tree.js +48 -1
- package/dist/simple-tree/tree.js.map +1 -1
- package/dist/simple-tree/treeNodeApi.d.ts +2 -75
- package/dist/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/dist/simple-tree/treeNodeApi.js +17 -25
- package/dist/simple-tree/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/treeNodeKernel.d.ts +26 -0
- package/dist/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/dist/simple-tree/treeNodeKernel.js +83 -0
- package/dist/simple-tree/treeNodeKernel.js.map +1 -0
- package/dist/simple-tree/types.d.ts +95 -3
- package/dist/simple-tree/types.d.ts.map +1 -1
- package/dist/simple-tree/types.js +120 -21
- package/dist/simple-tree/types.js.map +1 -1
- package/dist/util/breakable.d.ts +83 -0
- package/dist/util/breakable.d.ts.map +1 -0
- package/dist/util/breakable.js +178 -0
- package/dist/util/breakable.js.map +1 -0
- package/dist/util/index.d.ts +3 -2
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +9 -2
- package/dist/util/index.js.map +1 -1
- package/dist/util/nestedMap.d.ts +17 -3
- package/dist/util/nestedMap.d.ts.map +1 -1
- package/dist/util/nestedMap.js +21 -1
- package/dist/util/nestedMap.js.map +1 -1
- package/dist/util/utils.d.ts +7 -0
- package/dist/util/utils.d.ts.map +1 -1
- package/dist/util/utils.js +15 -1
- package/dist/util/utils.js.map +1 -1
- package/internal.d.ts +1 -1
- package/lib/beta.d.ts +1 -1
- package/lib/core/forest/editableForest.d.ts +6 -3
- package/lib/core/forest/editableForest.d.ts.map +1 -1
- package/lib/core/forest/editableForest.js +17 -5
- package/lib/core/forest/editableForest.js.map +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +1 -1
- package/lib/core/index.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 +2 -0
- package/lib/core/rebase/types.d.ts.map +1 -1
- package/lib/core/rebase/types.js +7 -1
- package/lib/core/rebase/types.js.map +1 -1
- package/lib/core/tree/anchorSet.d.ts +1 -0
- package/lib/core/tree/anchorSet.d.ts.map +1 -1
- package/lib/core/tree/anchorSet.js +13 -0
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/core/tree/detachedFieldIndex.d.ts +48 -11
- package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndex.js +145 -21
- package/lib/core/tree/detachedFieldIndex.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.js +13 -4
- package/lib/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- 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 -1
- package/lib/core/tree/index.d.ts.map +1 -1
- package/lib/core/tree/index.js.map +1 -1
- package/lib/core/tree/visitDelta.d.ts +3 -1
- package/lib/core/tree/visitDelta.d.ts.map +1 -1
- package/lib/core/tree/visitDelta.js +31 -15
- package/lib/core/tree/visitDelta.js.map +1 -1
- package/lib/core/tree/visitorUtils.d.ts +3 -3
- package/lib/core/tree/visitorUtils.d.ts.map +1 -1
- package/lib/core/tree/visitorUtils.js +4 -4
- package/lib/core/tree/visitorUtils.js.map +1 -1
- package/lib/events/events.d.ts +4 -1
- package/lib/events/events.d.ts.map +1 -1
- package/lib/events/events.js.map +1 -1
- package/lib/feature-libraries/default-schema/defaultEditBuilder.js +1 -1
- 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 +1 -0
- package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
- package/lib/feature-libraries/editableTreeBinder.js +1 -1
- package/lib/feature-libraries/editableTreeBinder.js.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -10
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js +2 -74
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -51
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/flex-tree/index.d.ts +3 -2
- package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/index.js +2 -1
- package/lib/feature-libraries/flex-tree/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts +1 -6
- package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +13 -34
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts +1 -5
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +3 -33
- package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +3 -3
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +3 -3
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts +11 -0
- package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts +96 -0
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -0
- package/lib/feature-libraries/modular-schema/discrepancies.js +260 -0
- package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -0
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +9 -2
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
- package/lib/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/genericFieldKind.js +3 -0
- package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts +2 -1
- package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/index.js +1 -0
- package/lib/feature-libraries/modular-schema/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +42 -26
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +51 -2
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +826 -247
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFormat.js +2 -0
- package/lib/feature-libraries/modular-schema/modularChangeFormat.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +44 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/lib/feature-libraries/node-key/index.d.ts +0 -1
- package/lib/feature-libraries/node-key/index.d.ts.map +1 -1
- package/lib/feature-libraries/node-key/index.js +0 -1
- package/lib/feature-libraries/node-key/index.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +5 -4
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
- package/lib/feature-libraries/optional-field/optionalField.js +1 -0
- package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
- package/lib/feature-libraries/sequence-field/index.d.ts +1 -1
- package/lib/feature-libraries/sequence-field/index.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/index.js +1 -1
- package/lib/feature-libraries/sequence-field/index.js.map +1 -1
- package/lib/feature-libraries/sequence-field/invert.js +1 -1
- package/lib/feature-libraries/sequence-field/invert.js.map +1 -1
- package/lib/feature-libraries/sequence-field/rebase.js +6 -1
- package/lib/feature-libraries/sequence-field/rebase.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 -1
- package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
- package/lib/feature-libraries/sequence-field/utils.d.ts +2 -17
- package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
- package/lib/feature-libraries/sequence-field/utils.js +31 -39
- package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
- package/lib/feature-libraries/typed-schema/typedTreeSchema.d.ts +1 -0
- package/lib/feature-libraries/typed-schema/typedTreeSchema.d.ts.map +1 -1
- package/lib/feature-libraries/typed-schema/typedTreeSchema.js +4 -2
- package/lib/feature-libraries/typed-schema/typedTreeSchema.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/shared-tree/schematizingTreeView.d.ts +4 -2
- package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/lib/shared-tree/schematizingTreeView.js +242 -185
- package/lib/shared-tree/schematizingTreeView.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +5 -1
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +158 -90
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/lib/shared-tree/treeApi.js +1 -1
- package/lib/shared-tree/treeApi.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +10 -1
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +47 -3
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree/treeView.d.ts.map +1 -1
- package/lib/shared-tree/treeView.js +4 -0
- package/lib/shared-tree/treeView.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +6 -0
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js +3 -0
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts +8 -6
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +273 -210
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/arrayNode.d.ts +4 -0
- package/lib/simple-tree/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/arrayNode.js +39 -22
- package/lib/simple-tree/arrayNode.js.map +1 -1
- package/lib/simple-tree/index.d.ts +3 -3
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js +1 -1
- package/lib/simple-tree/index.js.map +1 -1
- package/lib/simple-tree/leafNodeSchema.d.ts +22 -1
- package/lib/simple-tree/leafNodeSchema.d.ts.map +1 -1
- package/lib/simple-tree/leafNodeSchema.js +1 -1
- package/lib/simple-tree/leafNodeSchema.js.map +1 -1
- package/lib/simple-tree/mapNode.d.ts.map +1 -1
- package/lib/simple-tree/mapNode.js.map +1 -1
- package/lib/simple-tree/objectNode.d.ts.map +1 -1
- package/lib/simple-tree/objectNode.js +3 -2
- package/lib/simple-tree/objectNode.js.map +1 -1
- package/lib/simple-tree/proxies.d.ts.map +1 -1
- package/lib/simple-tree/proxies.js +9 -25
- package/lib/simple-tree/proxies.js.map +1 -1
- package/lib/simple-tree/proxyBinding.d.ts +4 -0
- package/lib/simple-tree/proxyBinding.d.ts.map +1 -1
- package/lib/simple-tree/proxyBinding.js +19 -0
- package/lib/simple-tree/proxyBinding.js.map +1 -1
- package/lib/simple-tree/schemaFactory.d.ts +16 -1
- package/lib/simple-tree/schemaFactory.d.ts.map +1 -1
- package/lib/simple-tree/schemaFactory.js +30 -3
- package/lib/simple-tree/schemaFactory.js.map +1 -1
- package/lib/simple-tree/schemaTypes.d.ts +36 -1
- package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
- package/lib/simple-tree/schemaTypes.js.map +1 -1
- package/lib/simple-tree/toFlexSchema.d.ts +2 -2
- package/lib/simple-tree/toFlexSchema.d.ts.map +1 -1
- package/lib/simple-tree/toFlexSchema.js +3 -2
- package/lib/simple-tree/toFlexSchema.js.map +1 -1
- package/lib/simple-tree/tree.d.ts +4 -1
- package/lib/simple-tree/tree.d.ts.map +1 -1
- package/lib/simple-tree/tree.js +44 -0
- package/lib/simple-tree/tree.js.map +1 -1
- package/lib/simple-tree/treeNodeApi.d.ts +2 -75
- package/lib/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/lib/simple-tree/treeNodeApi.js +20 -28
- package/lib/simple-tree/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/treeNodeKernel.d.ts +26 -0
- package/lib/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/lib/simple-tree/treeNodeKernel.js +79 -0
- package/lib/simple-tree/treeNodeKernel.js.map +1 -0
- package/lib/simple-tree/types.d.ts +95 -3
- package/lib/simple-tree/types.d.ts.map +1 -1
- package/lib/simple-tree/types.js +121 -22
- package/lib/simple-tree/types.js.map +1 -1
- package/lib/util/breakable.d.ts +83 -0
- package/lib/util/breakable.d.ts.map +1 -0
- package/lib/util/breakable.js +171 -0
- package/lib/util/breakable.js.map +1 -0
- package/lib/util/index.d.ts +3 -2
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +3 -2
- package/lib/util/index.js.map +1 -1
- package/lib/util/nestedMap.d.ts +17 -3
- package/lib/util/nestedMap.d.ts.map +1 -1
- package/lib/util/nestedMap.js +19 -0
- package/lib/util/nestedMap.js.map +1 -1
- package/lib/util/utils.d.ts +7 -0
- package/lib/util/utils.d.ts.map +1 -1
- package/lib/util/utils.js +13 -0
- package/lib/util/utils.js.map +1 -1
- package/package.json +29 -27
- package/src/core/forest/editableForest.ts +25 -4
- package/src/core/index.ts +2 -0
- package/src/core/rebase/index.ts +2 -0
- package/src/core/rebase/types.ts +17 -0
- package/src/core/tree/anchorSet.ts +14 -0
- package/src/core/tree/detachedFieldIndex.ts +217 -35
- package/src/core/tree/detachedFieldIndexCodec.ts +17 -8
- package/src/core/tree/detachedFieldIndexFormat.ts +1 -1
- package/src/core/tree/detachedFieldIndexTypes.ts +41 -5
- package/src/core/tree/index.ts +2 -1
- package/src/core/tree/visitDelta.ts +58 -16
- package/src/core/tree/visitorUtils.ts +7 -4
- package/src/events/events.ts +4 -2
- package/src/feature-libraries/default-schema/defaultEditBuilder.ts +1 -1
- package/src/feature-libraries/default-schema/defaultFieldKinds.ts +1 -0
- package/src/feature-libraries/editableTreeBinder.ts +1 -1
- package/src/feature-libraries/flex-map-tree/mapTreeNode.ts +1 -95
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +0 -62
- package/src/feature-libraries/flex-tree/index.ts +7 -2
- package/src/feature-libraries/flex-tree/lazyEntity.ts +0 -3
- package/src/feature-libraries/flex-tree/lazyField.ts +15 -47
- package/src/feature-libraries/flex-tree/lazyNode.ts +1 -48
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +1 -0
- package/src/feature-libraries/index.ts +4 -2
- package/src/feature-libraries/modular-schema/crossFieldQueries.ts +18 -0
- package/src/feature-libraries/modular-schema/discrepancies.ts +395 -0
- package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +10 -2
- package/src/feature-libraries/modular-schema/genericFieldKind.ts +3 -0
- package/src/feature-libraries/modular-schema/index.ts +2 -0
- package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +81 -35
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +1521 -444
- package/src/feature-libraries/modular-schema/modularChangeFormat.ts +2 -0
- package/src/feature-libraries/modular-schema/modularChangeTypes.ts +51 -0
- package/src/feature-libraries/node-key/index.ts +0 -1
- package/src/feature-libraries/object-forest/objectForest.ts +7 -3
- package/src/feature-libraries/optional-field/optionalField.ts +1 -0
- package/src/feature-libraries/sequence-field/index.ts +0 -2
- package/src/feature-libraries/sequence-field/invert.ts +1 -1
- package/src/feature-libraries/sequence-field/rebase.ts +7 -1
- package/src/feature-libraries/sequence-field/sequenceFieldChangeHandler.ts +2 -1
- package/src/feature-libraries/sequence-field/utils.ts +37 -85
- package/src/feature-libraries/typed-schema/typedTreeSchema.ts +10 -0
- package/src/index.ts +0 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/schematizingTreeView.ts +6 -2
- package/src/shared-tree/sharedTree.ts +12 -1
- package/src/shared-tree/sharedTreeChangeEnricher.ts +1 -1
- package/src/shared-tree/treeApi.ts +1 -1
- package/src/shared-tree/treeCheckout.ts +60 -5
- package/src/shared-tree/treeView.ts +5 -0
- package/src/shared-tree-core/branch.ts +10 -0
- package/src/shared-tree-core/sharedTreeCore.ts +25 -6
- package/src/simple-tree/arrayNode.ts +50 -23
- package/src/simple-tree/index.ts +3 -3
- package/src/simple-tree/leafNodeSchema.ts +1 -1
- package/src/simple-tree/mapNode.ts +2 -2
- package/src/simple-tree/objectNode.ts +9 -3
- package/src/simple-tree/proxies.ts +10 -33
- package/src/simple-tree/proxyBinding.ts +23 -0
- package/src/simple-tree/schemaFactory.ts +37 -2
- package/src/simple-tree/schemaTypes.ts +36 -1
- package/src/simple-tree/toFlexSchema.ts +5 -4
- package/src/simple-tree/tree.ts +68 -4
- package/src/simple-tree/treeNodeApi.ts +29 -111
- package/src/simple-tree/treeNodeKernel.ts +91 -0
- package/src/simple-tree/types.ts +292 -31
- package/src/util/breakable.ts +214 -0
- package/src/util/index.ts +11 -0
- package/src/util/nestedMap.ts +33 -3
- package/src/util/utils.ts +17 -0
- package/dist/feature-libraries/node-key/nodeKeyIndex.d.ts +0 -41
- package/dist/feature-libraries/node-key/nodeKeyIndex.d.ts.map +0 -1
- package/dist/feature-libraries/node-key/nodeKeyIndex.js +0 -101
- package/dist/feature-libraries/node-key/nodeKeyIndex.js.map +0 -1
- package/lib/feature-libraries/node-key/nodeKeyIndex.d.ts +0 -41
- package/lib/feature-libraries/node-key/nodeKeyIndex.d.ts.map +0 -1
- package/lib/feature-libraries/node-key/nodeKeyIndex.js +0 -97
- package/lib/feature-libraries/node-key/nodeKeyIndex.js.map +0 -1
- package/src/feature-libraries/node-key/nodeKeyIndex.ts +0 -132
|
@@ -35,15 +35,17 @@ import {
|
|
|
35
35
|
makeAnonChange,
|
|
36
36
|
makeDetachedNodeId,
|
|
37
37
|
mapCursorField,
|
|
38
|
+
replaceAtomRevisions,
|
|
38
39
|
revisionMetadataSourceFromInfo,
|
|
40
|
+
setInChangeAtomIdMap,
|
|
41
|
+
areEqualChangeAtomIds,
|
|
42
|
+
getFromChangeAtomIdMap,
|
|
39
43
|
type ChangeAtomId,
|
|
40
44
|
} from "../../core/index.js";
|
|
41
45
|
import {
|
|
42
46
|
type IdAllocationState,
|
|
43
47
|
type IdAllocator,
|
|
44
48
|
type Mutable,
|
|
45
|
-
type NestedSet,
|
|
46
|
-
addToNestedSet,
|
|
47
49
|
brand,
|
|
48
50
|
deleteFromNestedMap,
|
|
49
51
|
fail,
|
|
@@ -53,10 +55,11 @@ import {
|
|
|
53
55
|
idAllocatorFromState,
|
|
54
56
|
nestedMapFromFlatList,
|
|
55
57
|
nestedMapToFlatList,
|
|
56
|
-
nestedSetContains,
|
|
57
58
|
populateNestedMap,
|
|
58
59
|
setInNestedMap,
|
|
59
60
|
tryGetFromNestedMap,
|
|
61
|
+
type NestedMap,
|
|
62
|
+
type RangeQueryResult,
|
|
60
63
|
} from "../../util/index.js";
|
|
61
64
|
import {
|
|
62
65
|
type TreeChunk,
|
|
@@ -80,19 +83,19 @@ import {
|
|
|
80
83
|
type RebaseRevisionMetadata,
|
|
81
84
|
} from "./fieldChangeHandler.js";
|
|
82
85
|
import { type FieldKindWithEditor, withEditor } from "./fieldKindWithEditor.js";
|
|
83
|
-
import {
|
|
84
|
-
convertGenericChange,
|
|
85
|
-
genericFieldKind,
|
|
86
|
-
newGenericChangeset,
|
|
87
|
-
} from "./genericFieldKind.js";
|
|
86
|
+
import { convertGenericChange, genericFieldKind } from "./genericFieldKind.js";
|
|
88
87
|
import type { GenericChangeset } from "./genericFieldKindTypes.js";
|
|
89
88
|
import type {
|
|
89
|
+
CrossFieldKeyRange,
|
|
90
|
+
CrossFieldKeyTable,
|
|
90
91
|
FieldChange,
|
|
91
92
|
FieldChangeMap,
|
|
92
93
|
FieldChangeset,
|
|
94
|
+
FieldId,
|
|
93
95
|
ModularChangeset,
|
|
94
96
|
NodeChangeset,
|
|
95
97
|
NodeId,
|
|
98
|
+
TupleBTree,
|
|
96
99
|
} from "./modularChangeTypes.js";
|
|
97
100
|
|
|
98
101
|
/**
|
|
@@ -127,56 +130,59 @@ export class ModularChangeFamily
|
|
|
127
130
|
* The returned `FieldChangeset`s may be a shallow copy of the input `FieldChange`s.
|
|
128
131
|
*/
|
|
129
132
|
private normalizeFieldChanges(
|
|
130
|
-
change1: FieldChange
|
|
131
|
-
change2: FieldChange
|
|
133
|
+
change1: FieldChange,
|
|
134
|
+
change2: FieldChange,
|
|
132
135
|
genId: IdAllocator,
|
|
133
136
|
revisionMetadata: RevisionMetadataSource,
|
|
134
137
|
): {
|
|
135
|
-
fieldKind:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
fieldKind: FieldKindIdentifier;
|
|
139
|
+
changeHandler: FieldChangeHandler<unknown>;
|
|
140
|
+
change1: FieldChangeset;
|
|
141
|
+
change2: FieldChangeset;
|
|
138
142
|
} {
|
|
139
143
|
// TODO: Handle the case where changes have conflicting field kinds
|
|
140
144
|
const kind =
|
|
141
|
-
change1
|
|
145
|
+
change1.fieldKind !== genericFieldKind.identifier
|
|
142
146
|
? change1.fieldKind
|
|
143
|
-
: change2
|
|
147
|
+
: change2.fieldKind;
|
|
144
148
|
|
|
145
149
|
if (kind === genericFieldKind.identifier) {
|
|
146
|
-
//
|
|
150
|
+
// Both changes are generic
|
|
147
151
|
return {
|
|
148
|
-
fieldKind: genericFieldKind,
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
fieldKind: genericFieldKind.identifier,
|
|
153
|
+
changeHandler: genericFieldKind.changeHandler,
|
|
154
|
+
change1: change1.change,
|
|
155
|
+
change2: change2.change,
|
|
151
156
|
};
|
|
152
157
|
}
|
|
153
158
|
const fieldKind = getFieldKind(this.fieldKinds, kind);
|
|
154
|
-
const
|
|
159
|
+
const changeHandler = fieldKind.changeHandler;
|
|
155
160
|
const normalizedChange1 = this.normalizeFieldChange(
|
|
156
161
|
change1,
|
|
157
|
-
|
|
162
|
+
changeHandler,
|
|
158
163
|
genId,
|
|
159
164
|
revisionMetadata,
|
|
160
165
|
);
|
|
161
166
|
const normalizedChange2 = this.normalizeFieldChange(
|
|
162
167
|
change2,
|
|
163
|
-
|
|
168
|
+
changeHandler,
|
|
164
169
|
genId,
|
|
165
170
|
revisionMetadata,
|
|
166
171
|
);
|
|
167
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
fieldKind: kind,
|
|
174
|
+
changeHandler,
|
|
175
|
+
change1: normalizedChange1,
|
|
176
|
+
change2: normalizedChange2,
|
|
177
|
+
};
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
private normalizeFieldChange<T>(
|
|
171
|
-
fieldChange: FieldChange
|
|
181
|
+
fieldChange: FieldChange,
|
|
172
182
|
handler: FieldChangeHandler<T>,
|
|
173
183
|
genId: IdAllocator,
|
|
174
184
|
revisionMetadata: RevisionMetadataSource,
|
|
175
|
-
): FieldChangeset
|
|
176
|
-
if (fieldChange === undefined) {
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
|
|
185
|
+
): FieldChangeset {
|
|
180
186
|
if (fieldChange.fieldKind !== genericFieldKind.identifier) {
|
|
181
187
|
return fieldChange.change;
|
|
182
188
|
}
|
|
@@ -205,10 +211,12 @@ export class ModularChangeFamily
|
|
|
205
211
|
const { revInfos, maxId } = getRevInfoFromTaggedChanges(changes);
|
|
206
212
|
const idState: IdAllocationState = { maxId };
|
|
207
213
|
|
|
208
|
-
|
|
209
|
-
(
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
if (changes.length === 0) {
|
|
215
|
+
return makeModularChangeset();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return changes.reduce((change1, change2) =>
|
|
219
|
+
makeAnonChange(this.composePair(change1, change2, revInfos, idState)),
|
|
212
220
|
).change;
|
|
213
221
|
}
|
|
214
222
|
|
|
@@ -218,106 +226,283 @@ export class ModularChangeFamily
|
|
|
218
226
|
revInfos: RevisionInfo[],
|
|
219
227
|
idState: IdAllocationState,
|
|
220
228
|
): ModularChangeset {
|
|
229
|
+
const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys } =
|
|
230
|
+
this.composeAllFields(change1.change, change2.change, revInfos, idState);
|
|
231
|
+
|
|
232
|
+
const { allBuilds, allDestroys, allRefreshers } = composeBuildsDestroysAndRefreshers([
|
|
233
|
+
change1,
|
|
234
|
+
change2,
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
return makeModularChangeset(
|
|
238
|
+
this.pruneFieldMap(fieldChanges, nodeChanges),
|
|
239
|
+
nodeChanges,
|
|
240
|
+
nodeToParent,
|
|
241
|
+
nodeAliases,
|
|
242
|
+
crossFieldKeys,
|
|
243
|
+
idState.maxId,
|
|
244
|
+
revInfos,
|
|
245
|
+
undefined,
|
|
246
|
+
allBuilds,
|
|
247
|
+
allDestroys,
|
|
248
|
+
allRefreshers,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private composeAllFields(
|
|
253
|
+
change1: ModularChangeset,
|
|
254
|
+
change2: ModularChangeset,
|
|
255
|
+
revInfos: RevisionInfo[],
|
|
256
|
+
idState: IdAllocationState,
|
|
257
|
+
): ModularChangesetContent {
|
|
258
|
+
if (hasConflicts(change1) && hasConflicts(change2)) {
|
|
259
|
+
return {
|
|
260
|
+
fieldChanges: new Map(),
|
|
261
|
+
nodeChanges: new Map(),
|
|
262
|
+
nodeToParent: new Map(),
|
|
263
|
+
nodeAliases: new Map(),
|
|
264
|
+
crossFieldKeys: newBTree(),
|
|
265
|
+
};
|
|
266
|
+
} else if (hasConflicts(change1)) {
|
|
267
|
+
return change2;
|
|
268
|
+
} else if (hasConflicts(change2)) {
|
|
269
|
+
return change1;
|
|
270
|
+
}
|
|
271
|
+
|
|
221
272
|
const genId: IdAllocator = idAllocatorFromState(idState);
|
|
222
273
|
const revisionMetadata: RevisionMetadataSource = revisionMetadataSourceFromInfo(revInfos);
|
|
223
274
|
|
|
224
|
-
const crossFieldTable = newComposeTable();
|
|
275
|
+
const crossFieldTable = newComposeTable(change1, change2);
|
|
276
|
+
|
|
277
|
+
// We merge nodeChanges, nodeToParent, and nodeAliases from the two changesets.
|
|
278
|
+
// The merged tables will have correct entries for all nodes which are only referenced in one of the input changesets.
|
|
279
|
+
// During composeFieldMaps and processInvalidatedElements we will find all nodes referenced in both input changesets
|
|
280
|
+
// and adjust these tables as necessary.
|
|
281
|
+
// Note that when merging these tables we may encounter key collisions and will arbitrarily drop values in that case.
|
|
282
|
+
// A collision for a node ID means that that node is referenced in both changesets
|
|
283
|
+
// (since we assume that if two changesets use the same node ID they are referring to the same node),
|
|
284
|
+
// therefore all collisions will be addressed when processing the intersection of the changesets.
|
|
285
|
+
const composedNodeChanges: ChangeAtomIdMap<NodeChangeset> = mergeNestedMaps(
|
|
286
|
+
change1.nodeChanges,
|
|
287
|
+
change2.nodeChanges,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const composedNodeToParent = mergeNestedMaps(change1.nodeToParent, change2.nodeToParent);
|
|
291
|
+
const composedNodeAliases: ChangeAtomIdMap<NodeId> = mergeNestedMaps(
|
|
292
|
+
change1.nodeAliases,
|
|
293
|
+
change2.nodeAliases,
|
|
294
|
+
);
|
|
225
295
|
|
|
226
296
|
const composedFields = this.composeFieldMaps(
|
|
227
|
-
|
|
228
|
-
|
|
297
|
+
change1.fieldChanges,
|
|
298
|
+
change2.fieldChanges,
|
|
229
299
|
genId,
|
|
230
300
|
crossFieldTable,
|
|
231
301
|
revisionMetadata,
|
|
232
302
|
);
|
|
233
303
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
304
|
+
this.processInvalidatedElements(
|
|
305
|
+
crossFieldTable,
|
|
306
|
+
composedFields,
|
|
307
|
+
composedNodeChanges,
|
|
308
|
+
composedNodeToParent,
|
|
309
|
+
composedNodeAliases,
|
|
310
|
+
genId,
|
|
311
|
+
revisionMetadata,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
// Currently no field kinds require making changes to cross-field keys during composition, so we can just merge the two tables.
|
|
315
|
+
const composedCrossFieldKeys = mergeBTrees(change1.crossFieldKeys, change2.crossFieldKeys);
|
|
316
|
+
return {
|
|
317
|
+
fieldChanges: composedFields,
|
|
318
|
+
nodeChanges: composedNodeChanges,
|
|
319
|
+
nodeToParent: composedNodeToParent,
|
|
320
|
+
nodeAliases: composedNodeAliases,
|
|
321
|
+
crossFieldKeys: brand(composedCrossFieldKeys),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private composeInvalidatedField(
|
|
326
|
+
fieldChange: FieldChange,
|
|
327
|
+
crossFieldTable: ComposeTable,
|
|
328
|
+
genId: IdAllocator,
|
|
329
|
+
revisionMetadata: RevisionMetadataSource,
|
|
330
|
+
): void {
|
|
331
|
+
const context = crossFieldTable.fieldToContext.get(fieldChange);
|
|
332
|
+
assert(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
|
|
333
|
+
const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
|
|
334
|
+
|
|
335
|
+
const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
|
|
336
|
+
const composeNodes = (child1: NodeId | undefined, child2: NodeId | undefined): NodeId => {
|
|
337
|
+
if (
|
|
338
|
+
child1 !== undefined &&
|
|
339
|
+
child2 !== undefined &&
|
|
340
|
+
getFromChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2) === undefined
|
|
341
|
+
) {
|
|
342
|
+
setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
|
|
343
|
+
crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return child1 ?? child2 ?? fail("Should not compose two undefined nodes");
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const amendedChange = rebaser.compose(
|
|
350
|
+
fieldChange1,
|
|
351
|
+
fieldChange2,
|
|
352
|
+
composeNodes,
|
|
353
|
+
genId,
|
|
354
|
+
new ComposeManager(crossFieldTable, fieldChange, false),
|
|
355
|
+
revisionMetadata,
|
|
356
|
+
);
|
|
357
|
+
composedChange.change = brand(amendedChange);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Updates everything in the composed output which may no longer be valid.
|
|
362
|
+
* This could be due to
|
|
363
|
+
* - discovering that two node changesets refer to the same node (`nodeIdsToCompose`)
|
|
364
|
+
* - a previously composed field being invalidated by a cross field effect (`invalidatedFields`)
|
|
365
|
+
* - a field which was copied directly from an input changeset being invalidated by a cross field effect
|
|
366
|
+
* (`affectedBaseFields` and `affectedNewFields`)
|
|
367
|
+
*
|
|
368
|
+
* Updating an element may invalidate further elements. This function runs until there is no more invalidation.
|
|
369
|
+
*/
|
|
370
|
+
private processInvalidatedElements(
|
|
371
|
+
table: ComposeTable,
|
|
372
|
+
composedFields: FieldChangeMap,
|
|
373
|
+
composedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
374
|
+
composedNodeToParent: ChangeAtomIdMap<FieldId>,
|
|
375
|
+
nodeAliases: ChangeAtomIdMap<NodeId>,
|
|
376
|
+
genId: IdAllocator,
|
|
377
|
+
metadata: RevisionMetadataSource,
|
|
378
|
+
): void {
|
|
379
|
+
const pending = table.pendingCompositions;
|
|
380
|
+
while (
|
|
381
|
+
table.invalidatedFields.size > 0 ||
|
|
382
|
+
pending.nodeIdsToCompose.length > 0 ||
|
|
383
|
+
pending.affectedBaseFields.length > 0 ||
|
|
384
|
+
pending.affectedNewFields.length > 0
|
|
385
|
+
) {
|
|
386
|
+
// Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
|
|
387
|
+
for (const [id1, id2] of pending.nodeIdsToCompose) {
|
|
388
|
+
this.composeNodesById(
|
|
389
|
+
table.baseChange.nodeChanges,
|
|
390
|
+
table.newChange.nodeChanges,
|
|
391
|
+
composedNodes,
|
|
392
|
+
composedNodeToParent,
|
|
393
|
+
nodeAliases,
|
|
394
|
+
id1,
|
|
395
|
+
id2,
|
|
396
|
+
genId,
|
|
397
|
+
table,
|
|
398
|
+
metadata,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
pending.nodeIdsToCompose.length = 0;
|
|
403
|
+
|
|
404
|
+
this.composeAffectedFields(
|
|
405
|
+
table,
|
|
406
|
+
table.baseChange,
|
|
407
|
+
true,
|
|
408
|
+
pending.affectedBaseFields,
|
|
409
|
+
composedFields,
|
|
410
|
+
composedNodes,
|
|
242
411
|
genId,
|
|
243
|
-
|
|
244
|
-
|
|
412
|
+
metadata,
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
this.composeAffectedFields(
|
|
416
|
+
table,
|
|
417
|
+
table.newChange,
|
|
418
|
+
false,
|
|
419
|
+
pending.affectedNewFields,
|
|
420
|
+
composedFields,
|
|
421
|
+
composedNodes,
|
|
422
|
+
genId,
|
|
423
|
+
metadata,
|
|
245
424
|
);
|
|
425
|
+
|
|
426
|
+
this.processInvalidatedCompositions(table, genId, metadata);
|
|
246
427
|
}
|
|
428
|
+
}
|
|
247
429
|
|
|
248
|
-
|
|
430
|
+
private processInvalidatedCompositions(
|
|
431
|
+
table: ComposeTable,
|
|
432
|
+
genId: IdAllocator,
|
|
433
|
+
metadata: RevisionMetadataSource,
|
|
434
|
+
): void {
|
|
435
|
+
const fieldsToUpdate = table.invalidatedFields;
|
|
436
|
+
table.invalidatedFields = new Set();
|
|
437
|
+
for (const fieldChange of fieldsToUpdate) {
|
|
438
|
+
this.composeInvalidatedField(fieldChange, table, genId, metadata);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
249
441
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Ensures that each field in `affectedFields` has been updated in the composition output.
|
|
444
|
+
* Any field which has already been composed is ignored.
|
|
445
|
+
* All other fields are optimistically assumed to not have any changes in the other input changeset.
|
|
446
|
+
*
|
|
447
|
+
* @param change - The changeset which contains the affected fields.
|
|
448
|
+
* This should be one of the two changesets being composed.
|
|
449
|
+
* @param areBaseFields - Whether the affected fields are part of the base changeset.
|
|
450
|
+
* If not, they are assumed to be part of the new changeset.
|
|
451
|
+
* @param affectedFields - The set of fields to process.
|
|
452
|
+
*/
|
|
453
|
+
private composeAffectedFields(
|
|
454
|
+
table: ComposeTable,
|
|
455
|
+
change: ModularChangeset,
|
|
456
|
+
areBaseFields: boolean,
|
|
457
|
+
affectedFields: BTree<FieldIdKey, true>,
|
|
458
|
+
composedFields: FieldChangeMap,
|
|
459
|
+
composedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
460
|
+
genId: IdAllocator,
|
|
461
|
+
metadata: RevisionMetadataSource,
|
|
462
|
+
): void {
|
|
463
|
+
for (const fieldIdKey of affectedFields.keys()) {
|
|
464
|
+
const fieldId = normalizeFieldId(fieldIdFromFieldIdKey(fieldIdKey), change.nodeAliases);
|
|
465
|
+
const fieldChange = fieldChangeFromId(change.fieldChanges, change.nodeChanges, fieldId);
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
table.fieldToContext.has(fieldChange) ||
|
|
469
|
+
table.newFieldToBaseField.has(fieldChange)
|
|
470
|
+
) {
|
|
471
|
+
// This function handles fields which were not part of the intersection of the two changesets but which need to be updated anyway.
|
|
472
|
+
// If we've already processed this field then either it is up to date
|
|
473
|
+
// or there is pending inval which will be handled in processInvalidatedCompositions.
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
277
476
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
genId,
|
|
283
|
-
newCrossFieldManager(crossFieldTable, fieldChange, false),
|
|
284
|
-
revisionMetadata,
|
|
285
|
-
);
|
|
286
|
-
composedChange.change = brand(amendedChange);
|
|
287
|
-
|
|
288
|
-
// Process any newly discovered nodes.
|
|
289
|
-
for (const [taggedId1, taggedId2] of crossFieldTable.nodeIdPairs) {
|
|
290
|
-
this.composeNodesById(
|
|
291
|
-
change1.change.nodeChanges,
|
|
292
|
-
change2.change.nodeChanges,
|
|
293
|
-
composedNodeChanges,
|
|
294
|
-
taggedId1,
|
|
295
|
-
taggedId2,
|
|
296
|
-
genId,
|
|
297
|
-
crossFieldTable,
|
|
298
|
-
revisionMetadata,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
477
|
+
const emptyChange = this.createEmptyFieldChange(fieldChange.fieldKind);
|
|
478
|
+
const [change1, change2] = areBaseFields
|
|
479
|
+
? [fieldChange, emptyChange]
|
|
480
|
+
: [emptyChange, fieldChange];
|
|
301
481
|
|
|
302
|
-
|
|
482
|
+
const composedField = this.composeFieldChanges(change1, change2, genId, table, metadata);
|
|
483
|
+
|
|
484
|
+
if (fieldId.nodeId === undefined) {
|
|
485
|
+
composedFields.set(fieldId.field, composedField);
|
|
486
|
+
continue;
|
|
303
487
|
}
|
|
304
|
-
}
|
|
305
488
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
change2,
|
|
309
|
-
]);
|
|
489
|
+
const nodeId =
|
|
490
|
+
getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId;
|
|
310
491
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
492
|
+
let nodeChangeset = nodeChangeFromId(composedNodes, nodeId);
|
|
493
|
+
if (!table.composedNodes.has(nodeChangeset)) {
|
|
494
|
+
nodeChangeset = cloneNodeChangeset(nodeChangeset);
|
|
495
|
+
setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (nodeChangeset.fieldChanges === undefined) {
|
|
499
|
+
nodeChangeset.fieldChanges = new Map();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
nodeChangeset.fieldChanges.set(fieldId.field, composedField);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
affectedFields.clear();
|
|
321
506
|
}
|
|
322
507
|
|
|
323
508
|
private composeFieldMaps(
|
|
@@ -328,89 +513,106 @@ export class ModularChangeFamily
|
|
|
328
513
|
revisionMetadata: RevisionMetadataSource,
|
|
329
514
|
): FieldChangeMap {
|
|
330
515
|
const composedFields: FieldChangeMap = new Map();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
fields.add(field);
|
|
516
|
+
if (change1 === undefined || change2 === undefined) {
|
|
517
|
+
return change1 ?? change2 ?? composedFields;
|
|
334
518
|
}
|
|
335
519
|
|
|
336
|
-
for (const field of
|
|
337
|
-
|
|
338
|
-
|
|
520
|
+
for (const [field, fieldChange1] of change1) {
|
|
521
|
+
const fieldChange2 = change2.get(field);
|
|
522
|
+
const composedField =
|
|
523
|
+
fieldChange2 !== undefined
|
|
524
|
+
? this.composeFieldChanges(
|
|
525
|
+
fieldChange1,
|
|
526
|
+
fieldChange2,
|
|
527
|
+
genId,
|
|
528
|
+
crossFieldTable,
|
|
529
|
+
revisionMetadata,
|
|
530
|
+
)
|
|
531
|
+
: fieldChange1;
|
|
339
532
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const fieldChange2 = change2?.get(field);
|
|
533
|
+
composedFields.set(field, composedField);
|
|
534
|
+
}
|
|
343
535
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const manager = newCrossFieldManager(crossFieldTable, fieldChange1 ?? fieldChange2);
|
|
351
|
-
const change1Normalized =
|
|
352
|
-
normalizedFieldChange1 ?? fieldKind.changeHandler.createEmpty();
|
|
353
|
-
const change2Normalized =
|
|
354
|
-
normalizedFieldChange2 ?? fieldKind.changeHandler.createEmpty();
|
|
355
|
-
|
|
356
|
-
const composedChange = fieldKind.changeHandler.rebaser.compose(
|
|
357
|
-
change1Normalized,
|
|
358
|
-
change2Normalized,
|
|
359
|
-
(child1, child2) => {
|
|
360
|
-
crossFieldTable.nodeIdPairs.push([child1, child2]);
|
|
361
|
-
if (child2 !== undefined) {
|
|
362
|
-
addToNestedSet(crossFieldTable.nodeIds, child2.revision, child2.localId);
|
|
363
|
-
}
|
|
364
|
-
return child1 ?? child2 ?? fail("Should not compose two undefined nodes");
|
|
365
|
-
},
|
|
366
|
-
genId,
|
|
367
|
-
manager,
|
|
368
|
-
revisionMetadata,
|
|
369
|
-
);
|
|
536
|
+
for (const [field, fieldChange2] of change2) {
|
|
537
|
+
if (change1 === undefined || !change1.has(field)) {
|
|
538
|
+
composedFields.set(field, fieldChange2);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
370
541
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
change: brand(composedChange),
|
|
374
|
-
};
|
|
542
|
+
return composedFields;
|
|
543
|
+
}
|
|
375
544
|
|
|
376
|
-
|
|
377
|
-
|
|
545
|
+
/**
|
|
546
|
+
* Returns the composition of the two input fields.
|
|
547
|
+
*
|
|
548
|
+
* Any nodes in this field which were modified by both changesets
|
|
549
|
+
* will be added to `crossFieldTable.pendingCompositions.nodeIdsToCompose`.
|
|
550
|
+
*
|
|
551
|
+
* Any fields which had cross-field information sent to them as part of this field composition
|
|
552
|
+
* will be added to either `affectedBaseFields` or `affectedNewFields` in `crossFieldTable.pendingCompositions`.
|
|
553
|
+
*
|
|
554
|
+
* Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
|
|
555
|
+
*/
|
|
556
|
+
private composeFieldChanges(
|
|
557
|
+
change1: FieldChange,
|
|
558
|
+
change2: FieldChange,
|
|
559
|
+
idAllocator: IdAllocator,
|
|
560
|
+
crossFieldTable: ComposeTable,
|
|
561
|
+
revisionMetadata: RevisionMetadataSource,
|
|
562
|
+
): FieldChange {
|
|
563
|
+
const {
|
|
564
|
+
fieldKind,
|
|
565
|
+
changeHandler,
|
|
566
|
+
change1: change1Normalized,
|
|
567
|
+
change2: change2Normalized,
|
|
568
|
+
} = this.normalizeFieldChanges(change1, change2, idAllocator, revisionMetadata);
|
|
569
|
+
|
|
570
|
+
const manager = new ComposeManager(crossFieldTable, change1);
|
|
571
|
+
|
|
572
|
+
const composedChange = changeHandler.rebaser.compose(
|
|
573
|
+
change1Normalized,
|
|
574
|
+
change2Normalized,
|
|
575
|
+
(child1, child2) => {
|
|
576
|
+
if (child1 !== undefined && child2 !== undefined) {
|
|
577
|
+
setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
|
|
578
|
+
crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
|
|
579
|
+
}
|
|
580
|
+
return child1 ?? child2 ?? fail("Should not compose two undefined nodes");
|
|
581
|
+
},
|
|
582
|
+
idAllocator,
|
|
583
|
+
manager,
|
|
584
|
+
revisionMetadata,
|
|
585
|
+
);
|
|
378
586
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
});
|
|
587
|
+
const composedField: FieldChange = {
|
|
588
|
+
fieldKind,
|
|
589
|
+
change: brand(composedChange),
|
|
590
|
+
};
|
|
384
591
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
592
|
+
crossFieldTable.fieldToContext.set(change1, {
|
|
593
|
+
change1: change1Normalized,
|
|
594
|
+
change2: change2Normalized,
|
|
595
|
+
composedChange: composedField,
|
|
596
|
+
});
|
|
388
597
|
|
|
389
|
-
|
|
598
|
+
crossFieldTable.newFieldToBaseField.set(change2, change1);
|
|
599
|
+
return composedField;
|
|
390
600
|
}
|
|
391
601
|
|
|
392
602
|
private composeNodesById(
|
|
393
603
|
nodeChanges1: ChangeAtomIdMap<NodeChangeset>,
|
|
394
604
|
nodeChanges2: ChangeAtomIdMap<NodeChangeset>,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
605
|
+
composedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
606
|
+
composedNodeToParent: ChangeAtomIdMap<FieldId>,
|
|
607
|
+
nodeAliases: ChangeAtomIdMap<NodeId>,
|
|
608
|
+
id1: NodeId,
|
|
609
|
+
id2: NodeId,
|
|
398
610
|
idAllocator: IdAllocator,
|
|
399
611
|
crossFieldTable: ComposeTable,
|
|
400
612
|
revisionMetadata: RevisionMetadataSource,
|
|
401
613
|
): void {
|
|
402
|
-
const nodeChangeset1 =
|
|
403
|
-
|
|
404
|
-
? tryGetFromNestedMap(nodeChanges1, id1.revision, id1.localId) ??
|
|
405
|
-
fail("Unknown node ID")
|
|
406
|
-
: {};
|
|
407
|
-
|
|
408
|
-
const nodeChangeset2 =
|
|
409
|
-
id2 !== undefined
|
|
410
|
-
? tryGetFromNestedMap(nodeChanges2, id2.revision, id2.localId) ??
|
|
411
|
-
fail("Unknown node ID")
|
|
412
|
-
: {};
|
|
413
|
-
|
|
614
|
+
const nodeChangeset1 = nodeChangeFromId(nodeChanges1, id1);
|
|
615
|
+
const nodeChangeset2 = nodeChangeFromId(nodeChanges2, id2);
|
|
414
616
|
const composedNodeChangeset = this.composeNodeChanges(
|
|
415
617
|
nodeChangeset1,
|
|
416
618
|
nodeChangeset2,
|
|
@@ -419,32 +621,37 @@ export class ModularChangeFamily
|
|
|
419
621
|
revisionMetadata,
|
|
420
622
|
);
|
|
421
623
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
624
|
+
setInChangeAtomIdMap(composedNodes, id1, composedNodeChangeset);
|
|
625
|
+
|
|
626
|
+
if (!areEqualChangeAtomIds(id1, id2)) {
|
|
627
|
+
deleteFromNestedMap(composedNodes, id2.revision, id2.localId);
|
|
628
|
+
deleteFromNestedMap(composedNodeToParent, id2.revision, id2.localId);
|
|
629
|
+
setInChangeAtomIdMap(nodeAliases, id2, id1);
|
|
630
|
+
|
|
631
|
+
// We need to delete id1 to avoid forming a cycle in case id1 already had an alias.
|
|
632
|
+
deleteFromNestedMap(nodeAliases, id1.revision, id1.localId);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
crossFieldTable.composedNodes.add(composedNodeChangeset);
|
|
429
636
|
}
|
|
430
637
|
|
|
431
638
|
private composeNodeChanges(
|
|
432
|
-
change1: NodeChangeset
|
|
433
|
-
change2: NodeChangeset
|
|
639
|
+
change1: NodeChangeset,
|
|
640
|
+
change2: NodeChangeset,
|
|
434
641
|
genId: IdAllocator,
|
|
435
642
|
crossFieldTable: ComposeTable,
|
|
436
643
|
revisionMetadata: RevisionMetadataSource,
|
|
437
644
|
): NodeChangeset {
|
|
438
|
-
const nodeExistsConstraint =
|
|
439
|
-
change1?.nodeExistsConstraint ?? change2?.nodeExistsConstraint;
|
|
645
|
+
const nodeExistsConstraint = change1.nodeExistsConstraint ?? change2.nodeExistsConstraint;
|
|
440
646
|
|
|
441
647
|
const composedFieldChanges = this.composeFieldMaps(
|
|
442
|
-
change1
|
|
443
|
-
change2
|
|
648
|
+
change1.fieldChanges,
|
|
649
|
+
change2.fieldChanges,
|
|
444
650
|
genId,
|
|
445
651
|
crossFieldTable,
|
|
446
652
|
revisionMetadata,
|
|
447
653
|
);
|
|
654
|
+
|
|
448
655
|
const composedNodeChange: NodeChangeset = {};
|
|
449
656
|
|
|
450
657
|
if (composedFieldChanges.size > 0) {
|
|
@@ -480,6 +687,9 @@ export class ModularChangeFamily
|
|
|
480
687
|
|
|
481
688
|
if ((change.change.constraintViolationCount ?? 0) > 0) {
|
|
482
689
|
return makeModularChangeset(
|
|
690
|
+
undefined,
|
|
691
|
+
undefined,
|
|
692
|
+
undefined,
|
|
483
693
|
undefined,
|
|
484
694
|
undefined,
|
|
485
695
|
change.change.maxId,
|
|
@@ -490,14 +700,13 @@ export class ModularChangeFamily
|
|
|
490
700
|
);
|
|
491
701
|
}
|
|
492
702
|
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
// TODO: add a getMax function to IdAllocator to make for a clearer contract.
|
|
497
|
-
const genId: IdAllocator = idAllocatorFromState(idState);
|
|
703
|
+
const genId: IdAllocator = idAllocatorFromMaxId(change.change.maxId ?? -1);
|
|
704
|
+
const invertedNodeToParent = cloneNestedMap(change.change.nodeToParent);
|
|
705
|
+
|
|
498
706
|
const crossFieldTable: InvertTable = {
|
|
499
707
|
...newCrossFieldTable<FieldChange>(),
|
|
500
708
|
originalFieldToContext: new Map(),
|
|
709
|
+
invertedNodeToParent,
|
|
501
710
|
};
|
|
502
711
|
|
|
503
712
|
const { revInfos } = getRevInfoFromTaggedChanges([change]);
|
|
@@ -505,6 +714,7 @@ export class ModularChangeFamily
|
|
|
505
714
|
|
|
506
715
|
const invertedFields = this.invertFieldMap(
|
|
507
716
|
change.change.fieldChanges,
|
|
717
|
+
undefined,
|
|
508
718
|
isRollback,
|
|
509
719
|
genId,
|
|
510
720
|
crossFieldTable,
|
|
@@ -519,6 +729,7 @@ export class ModularChangeFamily
|
|
|
519
729
|
localId,
|
|
520
730
|
this.invertNodeChange(
|
|
521
731
|
nodeChangeset,
|
|
732
|
+
{ revision, localId },
|
|
522
733
|
isRollback,
|
|
523
734
|
genId,
|
|
524
735
|
crossFieldTable,
|
|
@@ -537,7 +748,7 @@ export class ModularChangeFamily
|
|
|
537
748
|
context !== undefined,
|
|
538
749
|
0x851 /* Should have context for every invalidated field */,
|
|
539
750
|
);
|
|
540
|
-
const { invertedField } = context;
|
|
751
|
+
const { invertedField, fieldId } = context;
|
|
541
752
|
|
|
542
753
|
const amendedChange = getChangeHandler(
|
|
543
754
|
this.fieldKinds,
|
|
@@ -546,17 +757,22 @@ export class ModularChangeFamily
|
|
|
546
757
|
originalFieldChange,
|
|
547
758
|
isRollback,
|
|
548
759
|
genId,
|
|
549
|
-
|
|
760
|
+
new InvertManager(crossFieldTable, fieldChange, fieldId),
|
|
550
761
|
revisionMetadata,
|
|
551
762
|
);
|
|
552
763
|
invertedField.change = brand(amendedChange);
|
|
553
764
|
}
|
|
554
765
|
}
|
|
555
766
|
|
|
767
|
+
const crossFieldKeys = this.makeCrossFieldKeyTable(invertedFields, invertedNodes);
|
|
768
|
+
|
|
556
769
|
return makeModularChangeset(
|
|
557
770
|
invertedFields,
|
|
558
771
|
invertedNodes,
|
|
559
|
-
|
|
772
|
+
invertedNodeToParent,
|
|
773
|
+
change.change.nodeAliases,
|
|
774
|
+
crossFieldKeys,
|
|
775
|
+
genId.getMaxId(),
|
|
560
776
|
[],
|
|
561
777
|
change.change.constraintViolationCount,
|
|
562
778
|
undefined,
|
|
@@ -566,6 +782,7 @@ export class ModularChangeFamily
|
|
|
566
782
|
|
|
567
783
|
private invertFieldMap(
|
|
568
784
|
changes: FieldChangeMap,
|
|
785
|
+
parentId: NodeId | undefined,
|
|
569
786
|
isRollback: boolean,
|
|
570
787
|
genId: IdAllocator,
|
|
571
788
|
crossFieldTable: InvertTable,
|
|
@@ -574,7 +791,8 @@ export class ModularChangeFamily
|
|
|
574
791
|
const invertedFields: FieldChangeMap = new Map();
|
|
575
792
|
|
|
576
793
|
for (const [field, fieldChange] of changes) {
|
|
577
|
-
const
|
|
794
|
+
const fieldId = { nodeId: parentId, field };
|
|
795
|
+
const manager = new InvertManager(crossFieldTable, fieldChange, fieldId);
|
|
578
796
|
const invertedChange = getChangeHandler(
|
|
579
797
|
this.fieldKinds,
|
|
580
798
|
fieldChange.fieldKind,
|
|
@@ -587,6 +805,7 @@ export class ModularChangeFamily
|
|
|
587
805
|
invertedFields.set(field, invertedFieldChange);
|
|
588
806
|
|
|
589
807
|
crossFieldTable.originalFieldToContext.set(fieldChange, {
|
|
808
|
+
fieldId,
|
|
590
809
|
invertedField: invertedFieldChange,
|
|
591
810
|
});
|
|
592
811
|
}
|
|
@@ -596,6 +815,7 @@ export class ModularChangeFamily
|
|
|
596
815
|
|
|
597
816
|
private invertNodeChange(
|
|
598
817
|
change: NodeChangeset,
|
|
818
|
+
id: NodeId,
|
|
599
819
|
isRollback: boolean,
|
|
600
820
|
genId: IdAllocator,
|
|
601
821
|
crossFieldTable: InvertTable,
|
|
@@ -606,6 +826,7 @@ export class ModularChangeFamily
|
|
|
606
826
|
if (change.fieldChanges !== undefined) {
|
|
607
827
|
inverse.fieldChanges = this.invertFieldMap(
|
|
608
828
|
change.fieldChanges,
|
|
829
|
+
id,
|
|
609
830
|
isRollback,
|
|
610
831
|
genId,
|
|
611
832
|
crossFieldTable,
|
|
@@ -625,11 +846,18 @@ export class ModularChangeFamily
|
|
|
625
846
|
const maxId = Math.max(change.maxId ?? -1, over.change.maxId ?? -1);
|
|
626
847
|
const idState: IdAllocationState = { maxId };
|
|
627
848
|
const genId: IdAllocator = idAllocatorFromState(idState);
|
|
849
|
+
|
|
628
850
|
const crossFieldTable: RebaseTable = {
|
|
629
851
|
...newCrossFieldTable<FieldChange>(),
|
|
630
|
-
|
|
631
|
-
|
|
852
|
+
newChange: change,
|
|
853
|
+
baseChange: over.change,
|
|
854
|
+
baseFieldToContext: new Map(),
|
|
855
|
+
baseToRebasedNodeId: new Map(),
|
|
856
|
+
rebasedFields: new Set(),
|
|
857
|
+
rebasedNodeToParent: cloneNestedMap(change.nodeToParent),
|
|
858
|
+
rebasedCrossFieldKeys: brand(change.crossFieldKeys.clone()),
|
|
632
859
|
nodeIdPairs: [],
|
|
860
|
+
affectedBaseFields: newBTree(),
|
|
633
861
|
};
|
|
634
862
|
|
|
635
863
|
let constraintState = newConstraintState(change.constraintViolationCount ?? 0);
|
|
@@ -643,51 +871,33 @@ export class ModularChangeFamily
|
|
|
643
871
|
getBaseRevisions,
|
|
644
872
|
};
|
|
645
873
|
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
genId,
|
|
874
|
+
const rebasedNodes: ChangeAtomIdMap<NodeChangeset> = cloneNestedMap(change.nodeChanges);
|
|
875
|
+
|
|
876
|
+
const rebasedFields = this.rebaseIntersectingFields(
|
|
650
877
|
crossFieldTable,
|
|
878
|
+
rebasedNodes,
|
|
879
|
+
genId,
|
|
880
|
+
constraintState,
|
|
651
881
|
rebaseMetadata,
|
|
652
882
|
);
|
|
653
883
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const baseNodeChange =
|
|
662
|
-
baseId !== undefined
|
|
663
|
-
? tryGetFromNestedMap(over.change.nodeChanges, baseId.revision, baseId.localId) ??
|
|
664
|
-
fail("Unknown node ID")
|
|
665
|
-
: {};
|
|
666
|
-
|
|
667
|
-
const rebasedNode = this.rebaseNodeChange(
|
|
668
|
-
newNodeChange,
|
|
669
|
-
baseNodeChange,
|
|
670
|
-
genId,
|
|
671
|
-
crossFieldTable,
|
|
672
|
-
rebaseMetadata,
|
|
673
|
-
);
|
|
674
|
-
|
|
675
|
-
if (rebasedNode !== undefined) {
|
|
676
|
-
const nodeId = newId ?? baseId ?? fail("Should not have two undefined IDs");
|
|
677
|
-
setInNestedMap(rebasedNodes, nodeId.revision, nodeId.localId, rebasedNode);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
884
|
+
this.rebaseFieldsWithoutNewChanges(
|
|
885
|
+
rebasedFields,
|
|
886
|
+
rebasedNodes,
|
|
887
|
+
crossFieldTable,
|
|
888
|
+
genId,
|
|
889
|
+
rebaseMetadata,
|
|
890
|
+
);
|
|
680
891
|
|
|
681
892
|
if (crossFieldTable.invalidatedFields.size > 0) {
|
|
682
893
|
const fieldsToUpdate = crossFieldTable.invalidatedFields;
|
|
683
894
|
crossFieldTable.invalidatedFields = new Set();
|
|
684
895
|
constraintState = newConstraintState(change.constraintViolationCount ?? 0);
|
|
685
896
|
for (const field of fieldsToUpdate) {
|
|
686
|
-
|
|
687
|
-
const context = crossFieldTable.fieldToContext.get(field);
|
|
897
|
+
const context = crossFieldTable.baseFieldToContext.get(field);
|
|
688
898
|
assert(context !== undefined, 0x852 /* Every field should have a context */);
|
|
689
899
|
const {
|
|
690
|
-
|
|
900
|
+
changeHandler,
|
|
691
901
|
change1: fieldChangeset,
|
|
692
902
|
change2: baseChangeset,
|
|
693
903
|
} = this.normalizeFieldChanges(
|
|
@@ -697,13 +907,34 @@ export class ModularChangeFamily
|
|
|
697
907
|
revisionMetadata,
|
|
698
908
|
);
|
|
699
909
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
910
|
+
const rebaseChild = (
|
|
911
|
+
curr: NodeId | undefined,
|
|
912
|
+
base: NodeId | undefined,
|
|
913
|
+
): NodeId | undefined => {
|
|
914
|
+
if (curr !== undefined) {
|
|
915
|
+
return curr;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (base !== undefined) {
|
|
919
|
+
for (const id of context.baseNodeIds) {
|
|
920
|
+
if (areEqualChangeAtomIds(base, id)) {
|
|
921
|
+
return base;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return undefined;
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
context.rebasedChange.change = brand(
|
|
930
|
+
changeHandler.rebaser.rebase(
|
|
931
|
+
fieldChangeset,
|
|
932
|
+
baseChangeset,
|
|
933
|
+
rebaseChild,
|
|
934
|
+
genId,
|
|
935
|
+
new RebaseManager(crossFieldTable, field, context.fieldId),
|
|
936
|
+
rebaseMetadata,
|
|
937
|
+
),
|
|
707
938
|
);
|
|
708
939
|
}
|
|
709
940
|
}
|
|
@@ -718,6 +949,9 @@ export class ModularChangeFamily
|
|
|
718
949
|
return makeModularChangeset(
|
|
719
950
|
this.pruneFieldMap(rebasedFields, rebasedNodes),
|
|
720
951
|
rebasedNodes,
|
|
952
|
+
crossFieldTable.rebasedNodeToParent,
|
|
953
|
+
change.nodeAliases,
|
|
954
|
+
crossFieldTable.rebasedCrossFieldKeys,
|
|
721
955
|
idState.maxId,
|
|
722
956
|
change.revisions,
|
|
723
957
|
constraintState.violationCount,
|
|
@@ -727,47 +961,283 @@ export class ModularChangeFamily
|
|
|
727
961
|
);
|
|
728
962
|
}
|
|
729
963
|
|
|
964
|
+
// This performs a first pass on all fields which have both new and base changes.
|
|
965
|
+
// TODO: Can we also handle additional passes in this method?
|
|
966
|
+
private rebaseIntersectingFields(
|
|
967
|
+
crossFieldTable: RebaseTable,
|
|
968
|
+
rebasedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
969
|
+
genId: IdAllocator,
|
|
970
|
+
constraintState: ConstraintState,
|
|
971
|
+
metadata: RebaseRevisionMetadata,
|
|
972
|
+
): FieldChangeMap {
|
|
973
|
+
const change = crossFieldTable.newChange;
|
|
974
|
+
const baseChange = crossFieldTable.baseChange;
|
|
975
|
+
const rebasedFields = this.rebaseFieldMap(
|
|
976
|
+
change.fieldChanges,
|
|
977
|
+
baseChange.fieldChanges,
|
|
978
|
+
undefined,
|
|
979
|
+
genId,
|
|
980
|
+
crossFieldTable,
|
|
981
|
+
metadata,
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// This loop processes all fields which have both base and new changes.
|
|
985
|
+
// Note that the call to `rebaseNodeChange` can add entries to `crossFieldTable.nodeIdPairs`.
|
|
986
|
+
for (const [newId, baseId, _attachState] of crossFieldTable.nodeIdPairs) {
|
|
987
|
+
const rebasedNode = this.rebaseNodeChange(
|
|
988
|
+
newId,
|
|
989
|
+
baseId,
|
|
990
|
+
genId,
|
|
991
|
+
crossFieldTable,
|
|
992
|
+
metadata,
|
|
993
|
+
constraintState,
|
|
994
|
+
);
|
|
995
|
+
|
|
996
|
+
setInChangeAtomIdMap(rebasedNodes, newId, rebasedNode);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return rebasedFields;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// This processes fields which have no new changes but have been invalidated by another field.
|
|
1003
|
+
private rebaseFieldsWithoutNewChanges(
|
|
1004
|
+
rebasedFields: FieldChangeMap,
|
|
1005
|
+
rebasedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
1006
|
+
crossFieldTable: RebaseTable,
|
|
1007
|
+
genId: IdAllocator,
|
|
1008
|
+
metadata: RebaseRevisionMetadata,
|
|
1009
|
+
): void {
|
|
1010
|
+
const baseChange = crossFieldTable.baseChange;
|
|
1011
|
+
for (const [revision, localId, fieldKey] of crossFieldTable.affectedBaseFields.keys()) {
|
|
1012
|
+
const baseNodeId =
|
|
1013
|
+
localId !== undefined
|
|
1014
|
+
? normalizeNodeId({ revision, localId }, baseChange.nodeAliases)
|
|
1015
|
+
: undefined;
|
|
1016
|
+
|
|
1017
|
+
const baseFieldChange = fieldMapFromNodeId(
|
|
1018
|
+
baseChange.fieldChanges,
|
|
1019
|
+
baseChange.nodeChanges,
|
|
1020
|
+
baseNodeId,
|
|
1021
|
+
).get(fieldKey);
|
|
1022
|
+
|
|
1023
|
+
assert(
|
|
1024
|
+
baseFieldChange !== undefined,
|
|
1025
|
+
0x9c2 /* Cross field key registered for empty field */,
|
|
1026
|
+
);
|
|
1027
|
+
if (crossFieldTable.baseFieldToContext.has(baseFieldChange)) {
|
|
1028
|
+
// This field has already been processed because there were changes to rebase.
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// This field has no changes in the new changeset, otherwise it would have been added to
|
|
1033
|
+
// `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
|
|
1034
|
+
const rebaseChild = (
|
|
1035
|
+
child: NodeId | undefined,
|
|
1036
|
+
baseChild: NodeId | undefined,
|
|
1037
|
+
stateChange: NodeAttachState | undefined,
|
|
1038
|
+
): NodeId | undefined => {
|
|
1039
|
+
assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
|
|
1040
|
+
return undefined;
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
|
|
1044
|
+
const fieldChange: FieldChange = {
|
|
1045
|
+
...baseFieldChange,
|
|
1046
|
+
change: brand(handler.createEmpty()),
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
const rebasedNodeId =
|
|
1050
|
+
baseNodeId !== undefined
|
|
1051
|
+
? rebasedNodeIdFromBaseNodeId(crossFieldTable, baseNodeId)
|
|
1052
|
+
: undefined;
|
|
1053
|
+
|
|
1054
|
+
const fieldId: FieldId = { nodeId: rebasedNodeId, field: fieldKey };
|
|
1055
|
+
const rebasedField: unknown = handler.rebaser.rebase(
|
|
1056
|
+
fieldChange.change,
|
|
1057
|
+
baseFieldChange.change,
|
|
1058
|
+
rebaseChild,
|
|
1059
|
+
genId,
|
|
1060
|
+
new RebaseManager(crossFieldTable, baseFieldChange, fieldId),
|
|
1061
|
+
metadata,
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
const rebasedFieldChange: FieldChange = {
|
|
1065
|
+
...baseFieldChange,
|
|
1066
|
+
change: brand(rebasedField),
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
// TODO: Deduplicate
|
|
1070
|
+
crossFieldTable.baseFieldToContext.set(baseFieldChange, {
|
|
1071
|
+
newChange: fieldChange,
|
|
1072
|
+
baseChange: baseFieldChange,
|
|
1073
|
+
rebasedChange: rebasedFieldChange,
|
|
1074
|
+
fieldId,
|
|
1075
|
+
baseNodeIds: [],
|
|
1076
|
+
});
|
|
1077
|
+
crossFieldTable.rebasedFields.add(rebasedFieldChange);
|
|
1078
|
+
|
|
1079
|
+
this.attachRebasedField(
|
|
1080
|
+
rebasedFields,
|
|
1081
|
+
rebasedNodes,
|
|
1082
|
+
crossFieldTable,
|
|
1083
|
+
rebasedFieldChange,
|
|
1084
|
+
fieldId,
|
|
1085
|
+
genId,
|
|
1086
|
+
metadata,
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
private attachRebasedField(
|
|
1092
|
+
rebasedFields: FieldChangeMap,
|
|
1093
|
+
rebasedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
1094
|
+
table: RebaseTable,
|
|
1095
|
+
rebasedField: FieldChange,
|
|
1096
|
+
{ nodeId, field: fieldKey }: FieldId,
|
|
1097
|
+
idAllocator: IdAllocator,
|
|
1098
|
+
metadata: RebaseRevisionMetadata,
|
|
1099
|
+
): void {
|
|
1100
|
+
if (nodeId === undefined) {
|
|
1101
|
+
rebasedFields.set(fieldKey, rebasedField);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const rebasedNode = getFromChangeAtomIdMap(rebasedNodes, nodeId);
|
|
1105
|
+
if (rebasedNode !== undefined) {
|
|
1106
|
+
if (rebasedNode.fieldChanges === undefined) {
|
|
1107
|
+
rebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
assert(!rebasedNode.fieldChanges.has(fieldKey), 0x9c4 /* Expected an empty field */);
|
|
1112
|
+
rebasedNode.fieldChanges.set(fieldKey, rebasedField);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const newNode: NodeChangeset = {
|
|
1117
|
+
fieldChanges: new Map([[fieldKey, rebasedField]]),
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
setInChangeAtomIdMap(rebasedNodes, nodeId, newNode);
|
|
1121
|
+
setInChangeAtomIdMap(table.baseToRebasedNodeId, nodeId, nodeId);
|
|
1122
|
+
|
|
1123
|
+
const parentFieldId = getParentFieldId(table.baseChange, nodeId);
|
|
1124
|
+
|
|
1125
|
+
this.attachRebasedNode(
|
|
1126
|
+
rebasedFields,
|
|
1127
|
+
rebasedNodes,
|
|
1128
|
+
table,
|
|
1129
|
+
nodeId,
|
|
1130
|
+
parentFieldId,
|
|
1131
|
+
idAllocator,
|
|
1132
|
+
metadata,
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
private attachRebasedNode(
|
|
1137
|
+
rebasedFields: FieldChangeMap,
|
|
1138
|
+
rebasedNodes: ChangeAtomIdMap<NodeChangeset>,
|
|
1139
|
+
table: RebaseTable,
|
|
1140
|
+
baseNodeId: NodeId,
|
|
1141
|
+
parentFieldIdBase: FieldId,
|
|
1142
|
+
idAllocator: IdAllocator,
|
|
1143
|
+
metadata: RebaseRevisionMetadata,
|
|
1144
|
+
): void {
|
|
1145
|
+
const baseFieldChange = fieldChangeFromId(
|
|
1146
|
+
table.baseChange.fieldChanges,
|
|
1147
|
+
table.baseChange.nodeChanges,
|
|
1148
|
+
parentFieldIdBase,
|
|
1149
|
+
);
|
|
1150
|
+
|
|
1151
|
+
const rebasedFieldId = rebasedFieldIdFromBaseId(table, parentFieldIdBase);
|
|
1152
|
+
setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, rebasedFieldId);
|
|
1153
|
+
|
|
1154
|
+
const context = table.baseFieldToContext.get(baseFieldChange);
|
|
1155
|
+
if (context !== undefined) {
|
|
1156
|
+
// We've already processed this field.
|
|
1157
|
+
// The new child node can be attached when processing invalidated fields.
|
|
1158
|
+
context.baseNodeIds.push(baseNodeId);
|
|
1159
|
+
table.invalidatedFields.add(baseFieldChange);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
|
|
1164
|
+
|
|
1165
|
+
const fieldChange: FieldChange = {
|
|
1166
|
+
...baseFieldChange,
|
|
1167
|
+
change: brand(handler.createEmpty()),
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1170
|
+
const rebasedChangeset = handler.rebaser.rebase(
|
|
1171
|
+
handler.createEmpty(),
|
|
1172
|
+
baseFieldChange.change,
|
|
1173
|
+
(_idNew, idBase) =>
|
|
1174
|
+
idBase !== undefined && areEqualChangeAtomIds(idBase, baseNodeId)
|
|
1175
|
+
? baseNodeId
|
|
1176
|
+
: undefined,
|
|
1177
|
+
idAllocator,
|
|
1178
|
+
new RebaseManager(table, baseFieldChange, rebasedFieldId),
|
|
1179
|
+
metadata,
|
|
1180
|
+
);
|
|
1181
|
+
|
|
1182
|
+
const rebasedField: FieldChange = { ...baseFieldChange, change: brand(rebasedChangeset) };
|
|
1183
|
+
table.rebasedFields.add(rebasedField);
|
|
1184
|
+
table.baseFieldToContext.set(baseFieldChange, {
|
|
1185
|
+
newChange: fieldChange,
|
|
1186
|
+
baseChange: baseFieldChange,
|
|
1187
|
+
rebasedChange: rebasedField,
|
|
1188
|
+
fieldId: rebasedFieldId,
|
|
1189
|
+
baseNodeIds: [],
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
this.attachRebasedField(
|
|
1193
|
+
rebasedFields,
|
|
1194
|
+
rebasedNodes,
|
|
1195
|
+
table,
|
|
1196
|
+
rebasedField,
|
|
1197
|
+
rebasedFieldId,
|
|
1198
|
+
idAllocator,
|
|
1199
|
+
metadata,
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
730
1203
|
private rebaseFieldMap(
|
|
731
1204
|
change: FieldChangeMap,
|
|
732
1205
|
over: FieldChangeMap,
|
|
1206
|
+
parentId: NodeId | undefined,
|
|
733
1207
|
genId: IdAllocator,
|
|
734
1208
|
crossFieldTable: RebaseTable,
|
|
735
1209
|
revisionMetadata: RebaseRevisionMetadata,
|
|
736
1210
|
): FieldChangeMap {
|
|
737
1211
|
const rebasedFields: FieldChangeMap = new Map();
|
|
1212
|
+
const rebaseChild = (
|
|
1213
|
+
child: NodeId | undefined,
|
|
1214
|
+
baseChild: NodeId | undefined,
|
|
1215
|
+
stateChange: NodeAttachState | undefined,
|
|
1216
|
+
): NodeId | undefined => {
|
|
1217
|
+
if (child !== undefined && baseChild !== undefined) {
|
|
1218
|
+
crossFieldTable.nodeIdPairs.push([child, baseChild, stateChange]);
|
|
1219
|
+
}
|
|
1220
|
+
return child;
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
for (const [field, fieldChange] of change) {
|
|
1224
|
+
const fieldId: FieldId = { nodeId: parentId, field };
|
|
1225
|
+
const baseChange = over.get(field);
|
|
1226
|
+
if (baseChange === undefined) {
|
|
1227
|
+
rebasedFields.set(field, fieldChange);
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
738
1230
|
|
|
739
|
-
// Rebase fields contained in the base changeset
|
|
740
|
-
for (const [field, baseChanges] of over) {
|
|
741
|
-
const fieldChange: FieldChange = change.get(field) ?? {
|
|
742
|
-
fieldKind: genericFieldKind.identifier,
|
|
743
|
-
change: brand(newGenericChangeset()),
|
|
744
|
-
};
|
|
745
1231
|
const {
|
|
746
1232
|
fieldKind,
|
|
1233
|
+
changeHandler,
|
|
747
1234
|
change1: fieldChangeset,
|
|
748
1235
|
change2: baseChangeset,
|
|
749
|
-
} = this.normalizeFieldChanges(fieldChange,
|
|
1236
|
+
} = this.normalizeFieldChanges(fieldChange, baseChange, genId, revisionMetadata);
|
|
750
1237
|
|
|
751
|
-
const manager =
|
|
1238
|
+
const manager = new RebaseManager(crossFieldTable, baseChange, fieldId);
|
|
752
1239
|
|
|
753
|
-
const
|
|
754
|
-
child: NodeId | undefined,
|
|
755
|
-
baseChild: NodeId | undefined,
|
|
756
|
-
_attachState: NodeAttachState | undefined,
|
|
757
|
-
): ChangeAtomId => {
|
|
758
|
-
crossFieldTable.nodeIdPairs.push([child, baseChild]);
|
|
759
|
-
return (
|
|
760
|
-
child ??
|
|
761
|
-
// The fact `child` is undefined means that the changeset to rebase does not include changes for
|
|
762
|
-
// this node or its descendants. However, it's possible that it will after rebasing.
|
|
763
|
-
// In that case, we will need a NodeId to represent these changes under in the rebased changeset.
|
|
764
|
-
// We adopt `baseChild` for this purpose.
|
|
765
|
-
baseChild ??
|
|
766
|
-
fail("Should not have two undefined node IDs")
|
|
767
|
-
);
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
const rebasedField = fieldKind.changeHandler.rebaser.rebase(
|
|
1240
|
+
const rebasedField = changeHandler.rebaser.rebase(
|
|
771
1241
|
fieldChangeset,
|
|
772
1242
|
baseChangeset,
|
|
773
1243
|
rebaseChild,
|
|
@@ -777,88 +1247,54 @@ export class ModularChangeFamily
|
|
|
777
1247
|
);
|
|
778
1248
|
|
|
779
1249
|
const rebasedFieldChange: FieldChange = {
|
|
780
|
-
fieldKind
|
|
1250
|
+
fieldKind,
|
|
781
1251
|
change: brand(rebasedField),
|
|
782
1252
|
};
|
|
783
1253
|
|
|
784
1254
|
rebasedFields.set(field, rebasedFieldChange);
|
|
785
1255
|
|
|
786
|
-
crossFieldTable.
|
|
787
|
-
baseChange
|
|
1256
|
+
crossFieldTable.baseFieldToContext.set(baseChange, {
|
|
1257
|
+
baseChange,
|
|
788
1258
|
newChange: fieldChange,
|
|
789
1259
|
rebasedChange: rebasedFieldChange,
|
|
1260
|
+
fieldId,
|
|
1261
|
+
baseNodeIds: [],
|
|
790
1262
|
});
|
|
791
|
-
}
|
|
792
1263
|
|
|
793
|
-
|
|
794
|
-
for (const [field, fieldChange] of change) {
|
|
795
|
-
if (!over?.has(field)) {
|
|
796
|
-
const baseChanges: FieldChange = {
|
|
797
|
-
fieldKind: genericFieldKind.identifier,
|
|
798
|
-
change: brand(newGenericChangeset()),
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
const {
|
|
802
|
-
fieldKind,
|
|
803
|
-
change1: fieldChangeset,
|
|
804
|
-
change2: baseChangeset,
|
|
805
|
-
} = this.normalizeFieldChanges(fieldChange, baseChanges, genId, revisionMetadata);
|
|
806
|
-
|
|
807
|
-
// TODO: Don't we need to add an entry in the context table?
|
|
808
|
-
const manager = newCrossFieldManager(crossFieldTable, fieldChange);
|
|
809
|
-
const rebasedChangeset = fieldKind.changeHandler.rebaser.rebase(
|
|
810
|
-
fieldChangeset,
|
|
811
|
-
baseChangeset,
|
|
812
|
-
(child, baseChild) => {
|
|
813
|
-
assert(
|
|
814
|
-
baseChild === undefined,
|
|
815
|
-
0x5b6 /* This field should not have any base changes */,
|
|
816
|
-
);
|
|
817
|
-
|
|
818
|
-
crossFieldTable.nodeIdPairs.push([child, undefined]);
|
|
819
|
-
|
|
820
|
-
return child;
|
|
821
|
-
},
|
|
822
|
-
genId,
|
|
823
|
-
manager,
|
|
824
|
-
revisionMetadata,
|
|
825
|
-
);
|
|
826
|
-
const rebasedFieldChange: FieldChange = {
|
|
827
|
-
fieldKind: fieldKind.identifier,
|
|
828
|
-
change: brand(rebasedChangeset),
|
|
829
|
-
};
|
|
830
|
-
rebasedFields.set(field, rebasedFieldChange);
|
|
831
|
-
}
|
|
1264
|
+
crossFieldTable.rebasedFields.add(rebasedFieldChange);
|
|
832
1265
|
}
|
|
833
1266
|
|
|
834
1267
|
return rebasedFields;
|
|
835
1268
|
}
|
|
836
1269
|
|
|
837
1270
|
private rebaseNodeChange(
|
|
838
|
-
|
|
839
|
-
|
|
1271
|
+
newId: NodeId,
|
|
1272
|
+
baseId: NodeId,
|
|
840
1273
|
genId: IdAllocator,
|
|
841
1274
|
crossFieldTable: RebaseTable,
|
|
842
1275
|
revisionMetadata: RebaseRevisionMetadata,
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}
|
|
1276
|
+
constraintState: ConstraintState,
|
|
1277
|
+
): NodeChangeset {
|
|
1278
|
+
const change = nodeChangeFromId(crossFieldTable.newChange.nodeChanges, newId);
|
|
1279
|
+
const over = nodeChangeFromId(crossFieldTable.baseChange.nodeChanges, baseId);
|
|
848
1280
|
|
|
849
1281
|
const baseMap: FieldChangeMap = over?.fieldChanges ?? new Map();
|
|
850
1282
|
|
|
851
|
-
const fieldChanges =
|
|
852
|
-
change
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1283
|
+
const fieldChanges =
|
|
1284
|
+
change.fieldChanges !== undefined && over.fieldChanges !== undefined
|
|
1285
|
+
? this.rebaseFieldMap(
|
|
1286
|
+
change?.fieldChanges ?? new Map(),
|
|
1287
|
+
baseMap,
|
|
1288
|
+
newId,
|
|
1289
|
+
genId,
|
|
1290
|
+
crossFieldTable,
|
|
1291
|
+
revisionMetadata,
|
|
1292
|
+
)
|
|
1293
|
+
: change.fieldChanges;
|
|
858
1294
|
|
|
859
1295
|
const rebasedChange: NodeChangeset = {};
|
|
860
1296
|
|
|
861
|
-
if (fieldChanges.size > 0) {
|
|
1297
|
+
if (fieldChanges !== undefined && fieldChanges.size > 0) {
|
|
862
1298
|
rebasedChange.fieldChanges = fieldChanges;
|
|
863
1299
|
}
|
|
864
1300
|
|
|
@@ -866,7 +1302,7 @@ export class ModularChangeFamily
|
|
|
866
1302
|
rebasedChange.nodeExistsConstraint = change.nodeExistsConstraint;
|
|
867
1303
|
}
|
|
868
1304
|
|
|
869
|
-
crossFieldTable.
|
|
1305
|
+
setInChangeAtomIdMap(crossFieldTable.baseToRebasedNodeId, baseId, newId);
|
|
870
1306
|
return rebasedChange;
|
|
871
1307
|
}
|
|
872
1308
|
|
|
@@ -915,9 +1351,13 @@ export class ModularChangeFamily
|
|
|
915
1351
|
}
|
|
916
1352
|
|
|
917
1353
|
private pruneFieldMap(
|
|
918
|
-
changeset: FieldChangeMap,
|
|
1354
|
+
changeset: FieldChangeMap | undefined,
|
|
919
1355
|
nodeMap: ChangeAtomIdMap<NodeChangeset>,
|
|
920
1356
|
): FieldChangeMap | undefined {
|
|
1357
|
+
if (changeset === undefined) {
|
|
1358
|
+
return undefined;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
921
1361
|
const prunedChangeset: FieldChangeMap = new Map();
|
|
922
1362
|
for (const [field, fieldChange] of changeset) {
|
|
923
1363
|
const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
|
|
@@ -938,9 +1378,7 @@ export class ModularChangeFamily
|
|
|
938
1378
|
nodeId: NodeId,
|
|
939
1379
|
nodeMap: ChangeAtomIdMap<NodeChangeset>,
|
|
940
1380
|
): NodeId | undefined {
|
|
941
|
-
const changeset =
|
|
942
|
-
assert(changeset !== undefined, 0x930 /* Unknown node ID */);
|
|
943
|
-
|
|
1381
|
+
const changeset = nodeChangeFromId(nodeMap, nodeId);
|
|
944
1382
|
const prunedFields =
|
|
945
1383
|
changeset.fieldChanges !== undefined
|
|
946
1384
|
? this.pruneFieldMap(changeset.fieldChanges, nodeMap)
|
|
@@ -955,7 +1393,7 @@ export class ModularChangeFamily
|
|
|
955
1393
|
deleteFromNestedMap(nodeMap, nodeId.revision, nodeId.localId);
|
|
956
1394
|
return undefined;
|
|
957
1395
|
} else {
|
|
958
|
-
|
|
1396
|
+
setInChangeAtomIdMap(nodeMap, nodeId, prunedChange);
|
|
959
1397
|
return nodeId;
|
|
960
1398
|
}
|
|
961
1399
|
}
|
|
@@ -984,10 +1422,32 @@ export class ModularChangeFamily
|
|
|
984
1422
|
]),
|
|
985
1423
|
);
|
|
986
1424
|
|
|
1425
|
+
const updatedNodeToParent: ChangeAtomIdMap<FieldId> = nestedMapFromFlatList(
|
|
1426
|
+
nestedMapToFlatList(change.nodeToParent).map(([revision, id, fieldId]) => [
|
|
1427
|
+
replaceRevision(revision, oldRevisions, newRevision),
|
|
1428
|
+
id,
|
|
1429
|
+
replaceFieldIdRevision(
|
|
1430
|
+
normalizeFieldId(fieldId, change.nodeAliases),
|
|
1431
|
+
oldRevisions,
|
|
1432
|
+
newRevision,
|
|
1433
|
+
),
|
|
1434
|
+
]),
|
|
1435
|
+
);
|
|
1436
|
+
|
|
987
1437
|
const updated: Mutable<ModularChangeset> = {
|
|
988
1438
|
...change,
|
|
989
1439
|
fieldChanges: updatedFields,
|
|
990
1440
|
nodeChanges: updatedNodes,
|
|
1441
|
+
nodeToParent: updatedNodeToParent,
|
|
1442
|
+
|
|
1443
|
+
// We've updated all references to old node IDs, so we no longer need an alias table.
|
|
1444
|
+
nodeAliases: new Map(),
|
|
1445
|
+
crossFieldKeys: replaceCrossFieldKeyTableRevisions(
|
|
1446
|
+
change.crossFieldKeys,
|
|
1447
|
+
oldRevisions,
|
|
1448
|
+
newRevision,
|
|
1449
|
+
change.nodeAliases,
|
|
1450
|
+
),
|
|
991
1451
|
};
|
|
992
1452
|
|
|
993
1453
|
if (change.builds !== undefined) {
|
|
@@ -1040,20 +1500,90 @@ export class ModularChangeFamily
|
|
|
1040
1500
|
): FieldChangeMap {
|
|
1041
1501
|
const updatedFields: FieldChangeMap = new Map();
|
|
1042
1502
|
for (const [field, fieldChange] of fields) {
|
|
1043
|
-
const updatedFieldChange =
|
|
1503
|
+
const updatedFieldChange = getChangeHandler(
|
|
1044
1504
|
this.fieldKinds,
|
|
1045
1505
|
fieldChange.fieldKind,
|
|
1046
|
-
).
|
|
1506
|
+
).rebaser.replaceRevisions(fieldChange.change, oldRevisions, newRevision);
|
|
1507
|
+
|
|
1508
|
+
updatedFields.set(field, { ...fieldChange, change: brand(updatedFieldChange) });
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
return updatedFields;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
private makeCrossFieldKeyTable(
|
|
1515
|
+
fields: FieldChangeMap,
|
|
1516
|
+
nodes: ChangeAtomIdMap<NodeChangeset>,
|
|
1517
|
+
): CrossFieldKeyTable {
|
|
1518
|
+
const keys: CrossFieldKeyTable = newCrossFieldKeyTable();
|
|
1519
|
+
this.populateCrossFieldKeyTableForFieldMap(keys, fields, undefined);
|
|
1520
|
+
forEachInNestedMap(nodes, (node, revision, localId) => {
|
|
1521
|
+
if (node.fieldChanges !== undefined) {
|
|
1522
|
+
this.populateCrossFieldKeyTableForFieldMap(keys, node.fieldChanges, {
|
|
1523
|
+
revision,
|
|
1524
|
+
localId,
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1047
1528
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1529
|
+
return keys;
|
|
1530
|
+
}
|
|
1050
1531
|
|
|
1051
|
-
|
|
1532
|
+
private populateCrossFieldKeyTableForFieldMap(
|
|
1533
|
+
table: CrossFieldKeyTable,
|
|
1534
|
+
fields: FieldChangeMap,
|
|
1535
|
+
parent: NodeId | undefined,
|
|
1536
|
+
): void {
|
|
1537
|
+
for (const [fieldKey, fieldChange] of fields) {
|
|
1538
|
+
const keys = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).getCrossFieldKeys(
|
|
1539
|
+
fieldChange.change,
|
|
1540
|
+
);
|
|
1541
|
+
for (const key of keys) {
|
|
1542
|
+
table.set(key, { nodeId: parent, field: fieldKey });
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1052
1545
|
}
|
|
1053
1546
|
|
|
1054
1547
|
public buildEditor(changeReceiver: (change: ModularChangeset) => void): ModularEditBuilder {
|
|
1055
|
-
return new ModularEditBuilder(this, changeReceiver);
|
|
1548
|
+
return new ModularEditBuilder(this, this.fieldKinds, changeReceiver);
|
|
1056
1549
|
}
|
|
1550
|
+
|
|
1551
|
+
private createEmptyFieldChange(fieldKind: FieldKindIdentifier): FieldChange {
|
|
1552
|
+
const emptyChange = getChangeHandler(this.fieldKinds, fieldKind).createEmpty();
|
|
1553
|
+
return { fieldKind, change: brand(emptyChange) };
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function replaceCrossFieldKeyTableRevisions(
|
|
1558
|
+
table: CrossFieldKeyTable,
|
|
1559
|
+
oldRevisions: Set<RevisionTag | undefined>,
|
|
1560
|
+
newRevision: RevisionTag | undefined,
|
|
1561
|
+
nodeAliases: ChangeAtomIdMap<NodeId>,
|
|
1562
|
+
): CrossFieldKeyTable {
|
|
1563
|
+
const updated: CrossFieldKeyTable = newBTree();
|
|
1564
|
+
table.forEachPair(([target, revision, id, count], field) => {
|
|
1565
|
+
const updatedKey: CrossFieldKeyRange = [
|
|
1566
|
+
target,
|
|
1567
|
+
replaceRevision(revision, oldRevisions, newRevision),
|
|
1568
|
+
id,
|
|
1569
|
+
count,
|
|
1570
|
+
];
|
|
1571
|
+
|
|
1572
|
+
const normalizedFieldId = normalizeFieldId(field, nodeAliases);
|
|
1573
|
+
const updatedNodeId =
|
|
1574
|
+
normalizedFieldId.nodeId !== undefined
|
|
1575
|
+
? replaceAtomRevisions(normalizedFieldId.nodeId, oldRevisions, newRevision)
|
|
1576
|
+
: undefined;
|
|
1577
|
+
|
|
1578
|
+
const updatedValue: FieldId = {
|
|
1579
|
+
...normalizedFieldId,
|
|
1580
|
+
nodeId: updatedNodeId,
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
updated.set(updatedKey, updatedValue);
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
return updated;
|
|
1057
1587
|
}
|
|
1058
1588
|
|
|
1059
1589
|
function replaceRevision(
|
|
@@ -1215,8 +1745,7 @@ function* relevantRemovedRootsFromFields(
|
|
|
1215
1745
|
for (const [_, fieldChange] of change) {
|
|
1216
1746
|
const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
|
|
1217
1747
|
const delegate = function* (node: NodeId): Iterable<DeltaDetachedNodeId> {
|
|
1218
|
-
const nodeChangeset =
|
|
1219
|
-
assert(nodeChangeset !== undefined, 0x931 /* Unknown node ID */);
|
|
1748
|
+
const nodeChangeset = nodeChangeFromId(nodeChanges, node);
|
|
1220
1749
|
if (nodeChangeset.fieldChanges !== undefined) {
|
|
1221
1750
|
yield* relevantRemovedRootsFromFields(
|
|
1222
1751
|
nodeChangeset.fieldChanges,
|
|
@@ -1297,6 +1826,9 @@ export function updateRefreshers(
|
|
|
1297
1826
|
return makeModularChangeset(
|
|
1298
1827
|
fieldChanges,
|
|
1299
1828
|
nodeChanges,
|
|
1829
|
+
change.nodeToParent,
|
|
1830
|
+
change.nodeAliases,
|
|
1831
|
+
change.crossFieldKeys,
|
|
1300
1832
|
maxId,
|
|
1301
1833
|
revisions,
|
|
1302
1834
|
constraintViolationCount,
|
|
@@ -1383,13 +1915,7 @@ function intoDeltaImpl(
|
|
|
1383
1915
|
const deltaField = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(
|
|
1384
1916
|
fieldChange.change,
|
|
1385
1917
|
(childChange): DeltaFieldMap => {
|
|
1386
|
-
const nodeChange =
|
|
1387
|
-
nodeChanges,
|
|
1388
|
-
childChange.revision,
|
|
1389
|
-
childChange.localId,
|
|
1390
|
-
);
|
|
1391
|
-
|
|
1392
|
-
assert(nodeChange !== undefined, 0x932 /* Unknown node ID */);
|
|
1918
|
+
const nodeChange = nodeChangeFromId(nodeChanges, childChange);
|
|
1393
1919
|
return deltaFromNodeChange(nodeChange, nodeChanges, idAllocator, fieldKinds);
|
|
1394
1920
|
},
|
|
1395
1921
|
idAllocator,
|
|
@@ -1482,68 +2008,100 @@ interface CrossFieldTable<TFieldData> {
|
|
|
1482
2008
|
|
|
1483
2009
|
interface InvertTable extends CrossFieldTable<FieldChange> {
|
|
1484
2010
|
originalFieldToContext: Map<FieldChange, InvertContext>;
|
|
2011
|
+
invertedNodeToParent: ChangeAtomIdMap<FieldId>;
|
|
1485
2012
|
}
|
|
1486
2013
|
|
|
1487
2014
|
interface InvertContext {
|
|
2015
|
+
fieldId: FieldId;
|
|
1488
2016
|
invertedField: FieldChange;
|
|
1489
2017
|
}
|
|
1490
2018
|
|
|
1491
2019
|
interface RebaseTable extends CrossFieldTable<FieldChange> {
|
|
2020
|
+
readonly baseChange: ModularChangeset;
|
|
2021
|
+
readonly newChange: ModularChangeset;
|
|
2022
|
+
|
|
1492
2023
|
/**
|
|
1493
|
-
* Maps from the FieldChange key used for the CrossFieldTable (which is the FieldChange
|
|
1494
|
-
* to context for the field.
|
|
1495
|
-
*/
|
|
1496
|
-
fieldToContext: Map<FieldChange, RebaseFieldContext>;
|
|
1497
|
-
/**
|
|
1498
|
-
* This map caches the output of a prior rebasing computation for a node, keyed on that computation's input.
|
|
1499
|
-
* The input for such a computation is characterized by a pair of node changesets:
|
|
1500
|
-
* - The node changeset from the input changeset being rebased
|
|
1501
|
-
* - The corresponding node changeset from the changeset being rebased over.
|
|
1502
|
-
*
|
|
1503
|
-
* Either of these may be undefined so we adopt the following convention:
|
|
1504
|
-
* - If the node changeset from the changeset being rebased is defined, then we use that as the key
|
|
1505
|
-
* - Otherwise, if the node changeset from the changeset being rebased over is defined, then we use that as the key
|
|
1506
|
-
* - Otherwise, we don't cache the output (which will be undefined anyway).
|
|
1507
|
-
*
|
|
1508
|
-
* This map is needed once we switch from the initial pass (which generates a new changeset) to the second pass which
|
|
1509
|
-
* performs surgery on the changeset generated in the first pass: we don't want to re-run the rebasing of nested
|
|
1510
|
-
* changes. Instead we want to keep using the objects generated in the first pass and mutate them where needed.
|
|
2024
|
+
* Maps from the FieldChange key used for the CrossFieldTable (which is the base FieldChange)
|
|
2025
|
+
* to the context for the field.
|
|
1511
2026
|
*/
|
|
1512
|
-
|
|
2027
|
+
readonly baseFieldToContext: Map<FieldChange, RebaseFieldContext>;
|
|
2028
|
+
readonly baseToRebasedNodeId: ChangeAtomIdMap<NodeId>;
|
|
2029
|
+
readonly rebasedFields: Set<FieldChange>;
|
|
2030
|
+
readonly rebasedNodeToParent: ChangeAtomIdMap<FieldId>;
|
|
2031
|
+
readonly rebasedCrossFieldKeys: CrossFieldKeyTable;
|
|
1513
2032
|
|
|
1514
2033
|
/**
|
|
1515
|
-
* List of (newId, baseId) pairs encountered so far.
|
|
2034
|
+
* List of unprocessed (newId, baseId) pairs encountered so far.
|
|
1516
2035
|
*/
|
|
1517
|
-
nodeIdPairs: [NodeId
|
|
2036
|
+
readonly nodeIdPairs: [NodeId, NodeId, NodeAttachState | undefined][];
|
|
2037
|
+
readonly affectedBaseFields: TupleBTree<FieldIdKey, boolean>;
|
|
1518
2038
|
}
|
|
1519
2039
|
|
|
2040
|
+
type FieldIdKey = [RevisionTag | undefined, ChangesetLocalId | undefined, FieldKey];
|
|
2041
|
+
|
|
1520
2042
|
interface RebaseFieldContext {
|
|
1521
2043
|
baseChange: FieldChange;
|
|
1522
2044
|
newChange: FieldChange;
|
|
1523
2045
|
rebasedChange: FieldChange;
|
|
2046
|
+
fieldId: FieldId;
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* The set of node IDs in the base changeset which should be included in the rebased field,
|
|
2050
|
+
* even if there is no corresponding node changeset in the new change.
|
|
2051
|
+
*/
|
|
2052
|
+
baseNodeIds: NodeId[];
|
|
1524
2053
|
}
|
|
1525
2054
|
|
|
1526
|
-
function newComposeTable(
|
|
2055
|
+
function newComposeTable(
|
|
2056
|
+
baseChange: ModularChangeset,
|
|
2057
|
+
newChange: ModularChangeset,
|
|
2058
|
+
): ComposeTable {
|
|
1527
2059
|
return {
|
|
1528
2060
|
...newCrossFieldTable<FieldChange>(),
|
|
2061
|
+
baseChange,
|
|
2062
|
+
newChange,
|
|
1529
2063
|
fieldToContext: new Map(),
|
|
1530
|
-
|
|
1531
|
-
|
|
2064
|
+
newFieldToBaseField: new Map(),
|
|
2065
|
+
newToBaseNodeId: new Map(),
|
|
2066
|
+
composedNodes: new Set(),
|
|
2067
|
+
pendingCompositions: {
|
|
2068
|
+
nodeIdsToCompose: [],
|
|
2069
|
+
affectedBaseFields: newBTree(),
|
|
2070
|
+
affectedNewFields: newBTree(),
|
|
2071
|
+
},
|
|
1532
2072
|
};
|
|
1533
2073
|
}
|
|
1534
2074
|
|
|
1535
2075
|
interface ComposeTable extends CrossFieldTable<FieldChange> {
|
|
2076
|
+
readonly baseChange: ModularChangeset;
|
|
2077
|
+
readonly newChange: ModularChangeset;
|
|
2078
|
+
|
|
1536
2079
|
/**
|
|
1537
2080
|
* Maps from an input changeset for a field (from change1 if it has one, from change2 otherwise) to the context for that field.
|
|
1538
2081
|
*/
|
|
1539
|
-
fieldToContext: Map<FieldChange, ComposeFieldContext>;
|
|
2082
|
+
readonly fieldToContext: Map<FieldChange, ComposeFieldContext>;
|
|
2083
|
+
readonly newFieldToBaseField: Map<FieldChange, FieldChange>;
|
|
2084
|
+
readonly newToBaseNodeId: ChangeAtomIdMap<NodeId>;
|
|
2085
|
+
readonly composedNodes: Set<NodeChangeset>;
|
|
2086
|
+
readonly pendingCompositions: PendingCompositions;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
interface PendingCompositions {
|
|
2090
|
+
/**
|
|
2091
|
+
* Each entry in this list represents a node with both base and new changes which have not yet been composed.
|
|
2092
|
+
* Entries are of the form [baseId, newId].
|
|
2093
|
+
*/
|
|
2094
|
+
readonly nodeIdsToCompose: [NodeId, NodeId][];
|
|
1540
2095
|
|
|
1541
2096
|
/**
|
|
1542
|
-
* The set of
|
|
2097
|
+
* The set of fields in the base changeset which have been affected by a cross field effect.
|
|
1543
2098
|
*/
|
|
1544
|
-
|
|
2099
|
+
readonly affectedBaseFields: BTree<FieldIdKey, true>;
|
|
1545
2100
|
|
|
1546
|
-
|
|
2101
|
+
/**
|
|
2102
|
+
* The set of fields in the new changeset which have been affected by a cross field effect.
|
|
2103
|
+
*/
|
|
2104
|
+
readonly affectedNewFields: BTree<FieldIdKey, true>;
|
|
1547
2105
|
}
|
|
1548
2106
|
|
|
1549
2107
|
interface ComposeFieldContext {
|
|
@@ -1575,77 +2133,269 @@ function newConstraintState(violationCount: number): ConstraintState {
|
|
|
1575
2133
|
};
|
|
1576
2134
|
}
|
|
1577
2135
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
2136
|
+
abstract class CrossFieldManagerI<T> implements CrossFieldManager {
|
|
2137
|
+
public constructor(
|
|
2138
|
+
protected readonly crossFieldTable: CrossFieldTable<T>,
|
|
2139
|
+
private readonly currentFieldKey: T,
|
|
2140
|
+
protected readonly allowInval = true,
|
|
2141
|
+
) {}
|
|
2142
|
+
|
|
2143
|
+
public set(
|
|
2144
|
+
target: CrossFieldTarget,
|
|
2145
|
+
revision: RevisionTag | undefined,
|
|
2146
|
+
id: ChangesetLocalId,
|
|
2147
|
+
count: number,
|
|
2148
|
+
newValue: unknown,
|
|
2149
|
+
invalidateDependents: boolean,
|
|
2150
|
+
): void {
|
|
2151
|
+
if (invalidateDependents && this.allowInval) {
|
|
2152
|
+
const lastChangedId = (id as number) + count - 1;
|
|
2153
|
+
let firstId = id;
|
|
2154
|
+
while (firstId <= lastChangedId) {
|
|
2155
|
+
const dependentEntry = getFirstFromCrossFieldMap(
|
|
2156
|
+
this.getDependents(target),
|
|
2157
|
+
revision,
|
|
2158
|
+
firstId,
|
|
2159
|
+
lastChangedId - firstId + 1,
|
|
2160
|
+
);
|
|
2161
|
+
if (dependentEntry.value !== undefined) {
|
|
2162
|
+
this.crossFieldTable.invalidatedFields.add(dependentEntry.value);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
firstId = brand(firstId + dependentEntry.length);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
setInCrossFieldMap(this.getMap(target), revision, id, count, newValue);
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
public get(
|
|
2172
|
+
target: CrossFieldTarget,
|
|
2173
|
+
revision: RevisionTag | undefined,
|
|
2174
|
+
id: ChangesetLocalId,
|
|
2175
|
+
count: number,
|
|
2176
|
+
addDependency: boolean,
|
|
2177
|
+
): RangeQueryResult<unknown> {
|
|
2178
|
+
if (addDependency) {
|
|
2179
|
+
// We assume that if there is already an entry for this ID it is because
|
|
2180
|
+
// a field handler has called compose on the same node multiple times.
|
|
2181
|
+
// In this case we only want to update the latest version, so we overwrite the dependency.
|
|
2182
|
+
setInCrossFieldMap(
|
|
2183
|
+
this.getDependents(target),
|
|
2184
|
+
revision,
|
|
2185
|
+
id,
|
|
2186
|
+
count,
|
|
2187
|
+
this.currentFieldKey,
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
return getFirstFromCrossFieldMap(this.getMap(target), revision, id, count);
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
public abstract onMoveIn(id: NodeId): void;
|
|
2194
|
+
|
|
2195
|
+
public abstract moveKey(
|
|
2196
|
+
target: CrossFieldTarget,
|
|
2197
|
+
revision: RevisionTag | undefined,
|
|
2198
|
+
id: ChangesetLocalId,
|
|
2199
|
+
count: number,
|
|
2200
|
+
): void;
|
|
2201
|
+
|
|
2202
|
+
private getMap(target: CrossFieldTarget): CrossFieldMap<unknown> {
|
|
2203
|
+
return target === CrossFieldTarget.Source
|
|
2204
|
+
? this.crossFieldTable.srcTable
|
|
2205
|
+
: this.crossFieldTable.dstTable;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
private getDependents(target: CrossFieldTarget): CrossFieldMap<T> {
|
|
2209
|
+
return target === CrossFieldTarget.Source
|
|
2210
|
+
? this.crossFieldTable.srcDependents
|
|
2211
|
+
: this.crossFieldTable.dstDependents;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
class InvertManager extends CrossFieldManagerI<FieldChange> {
|
|
2216
|
+
public constructor(
|
|
2217
|
+
table: InvertTable,
|
|
2218
|
+
field: FieldChange,
|
|
2219
|
+
private readonly fieldId: FieldId,
|
|
2220
|
+
allowInval = true,
|
|
2221
|
+
) {
|
|
2222
|
+
super(table, field, allowInval);
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
public override onMoveIn(id: ChangeAtomId): void {
|
|
2226
|
+
setInChangeAtomIdMap(this.table.invertedNodeToParent, id, this.fieldId);
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
public override moveKey(
|
|
2230
|
+
target: CrossFieldTarget,
|
|
2231
|
+
revision: RevisionTag | undefined,
|
|
2232
|
+
id: ChangesetLocalId,
|
|
2233
|
+
count: number,
|
|
2234
|
+
): void {
|
|
2235
|
+
assert(false, 0x9c5 /* Keys should not be moved manually during invert */);
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
private get table(): InvertTable {
|
|
2239
|
+
return this.crossFieldTable as InvertTable;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
class RebaseManager extends CrossFieldManagerI<FieldChange> {
|
|
2244
|
+
public constructor(
|
|
2245
|
+
table: RebaseTable,
|
|
2246
|
+
currentField: FieldChange,
|
|
2247
|
+
private readonly fieldId: FieldId,
|
|
2248
|
+
allowInval = true,
|
|
2249
|
+
) {
|
|
2250
|
+
super(table, currentField, allowInval);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
public override set(
|
|
2254
|
+
target: CrossFieldTarget,
|
|
2255
|
+
revision: RevisionTag | undefined,
|
|
2256
|
+
id: ChangesetLocalId,
|
|
2257
|
+
count: number,
|
|
2258
|
+
newValue: unknown,
|
|
2259
|
+
invalidateDependents: boolean,
|
|
2260
|
+
): void {
|
|
2261
|
+
if (invalidateDependents && this.allowInval) {
|
|
2262
|
+
const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, [
|
|
2263
|
+
target,
|
|
2264
|
+
revision,
|
|
2265
|
+
id,
|
|
2266
|
+
count,
|
|
2267
|
+
]);
|
|
2268
|
+
|
|
2269
|
+
assert(
|
|
2270
|
+
newFieldIds.length === 0,
|
|
2271
|
+
0x9c6 /* TODO: Modifying a cross-field key from the new changeset is currently unsupported */,
|
|
2272
|
+
);
|
|
2273
|
+
|
|
2274
|
+
const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, [
|
|
2275
|
+
target,
|
|
2276
|
+
revision,
|
|
2277
|
+
id,
|
|
2278
|
+
count,
|
|
2279
|
+
]);
|
|
2280
|
+
|
|
2281
|
+
assert(
|
|
2282
|
+
baseFieldIds.length > 0,
|
|
2283
|
+
0x9c7 /* Cross field key not registered in base or new change */,
|
|
2284
|
+
);
|
|
2285
|
+
|
|
2286
|
+
for (const baseFieldId of baseFieldIds) {
|
|
2287
|
+
this.table.affectedBaseFields.set(
|
|
2288
|
+
[baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field],
|
|
2289
|
+
true,
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
super.set(target, revision, id, count, newValue, invalidateDependents);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
public override onMoveIn(id: ChangeAtomId): void {
|
|
2298
|
+
setInChangeAtomIdMap(this.table.rebasedNodeToParent, id, this.fieldId);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
public override moveKey(
|
|
2302
|
+
target: CrossFieldTarget,
|
|
2303
|
+
revision: RevisionTag | undefined,
|
|
2304
|
+
id: ChangesetLocalId,
|
|
2305
|
+
count: number,
|
|
2306
|
+
): void {
|
|
2307
|
+
setInCrossFieldKeyTable(
|
|
2308
|
+
this.table.rebasedCrossFieldKeys,
|
|
2309
|
+
target,
|
|
2310
|
+
revision,
|
|
2311
|
+
id,
|
|
2312
|
+
count,
|
|
2313
|
+
this.fieldId,
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
private get table(): RebaseTable {
|
|
2318
|
+
return this.crossFieldTable as RebaseTable;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
// TODO: Deduplicate this with RebaseTable
|
|
2323
|
+
class ComposeManager extends CrossFieldManagerI<FieldChange> {
|
|
2324
|
+
public constructor(table: ComposeTable, currentField: FieldChange, allowInval = true) {
|
|
2325
|
+
super(table, currentField, allowInval);
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
public override set(
|
|
2329
|
+
target: CrossFieldTarget,
|
|
2330
|
+
revision: RevisionTag | undefined,
|
|
2331
|
+
id: ChangesetLocalId,
|
|
2332
|
+
count: number,
|
|
2333
|
+
newValue: unknown,
|
|
2334
|
+
invalidateDependents: boolean,
|
|
2335
|
+
): void {
|
|
2336
|
+
if (invalidateDependents && this.allowInval) {
|
|
2337
|
+
const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, [
|
|
2338
|
+
target,
|
|
2339
|
+
revision,
|
|
2340
|
+
id,
|
|
2341
|
+
count,
|
|
2342
|
+
]);
|
|
2343
|
+
|
|
2344
|
+
if (newFieldIds.length > 0) {
|
|
2345
|
+
for (const newFieldId of newFieldIds) {
|
|
2346
|
+
this.table.pendingCompositions.affectedNewFields.set(
|
|
2347
|
+
[newFieldId.nodeId?.revision, newFieldId.nodeId?.localId, newFieldId.field],
|
|
2348
|
+
true,
|
|
1615
2349
|
);
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
2350
|
+
}
|
|
2351
|
+
} else {
|
|
2352
|
+
const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, [
|
|
2353
|
+
target,
|
|
2354
|
+
revision,
|
|
2355
|
+
id,
|
|
2356
|
+
count,
|
|
2357
|
+
]);
|
|
1619
2358
|
|
|
1620
|
-
|
|
2359
|
+
assert(
|
|
2360
|
+
baseFieldIds.length > 0,
|
|
2361
|
+
0x9c8 /* Cross field key not registered in base or new change */,
|
|
2362
|
+
);
|
|
2363
|
+
|
|
2364
|
+
for (const baseFieldId of baseFieldIds) {
|
|
2365
|
+
this.table.pendingCompositions.affectedBaseFields.set(
|
|
2366
|
+
[baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field],
|
|
2367
|
+
true,
|
|
2368
|
+
);
|
|
1621
2369
|
}
|
|
1622
2370
|
}
|
|
1623
|
-
|
|
1624
|
-
},
|
|
2371
|
+
}
|
|
1625
2372
|
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
},
|
|
1641
|
-
};
|
|
2373
|
+
super.set(target, revision, id, count, newValue, invalidateDependents);
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
public override onMoveIn(id: ChangeAtomId): void {
|
|
2377
|
+
throw new Error("Method not implemented.");
|
|
2378
|
+
}
|
|
2379
|
+
public override moveKey(
|
|
2380
|
+
target: CrossFieldTarget,
|
|
2381
|
+
revision: RevisionTag | undefined,
|
|
2382
|
+
id: ChangesetLocalId,
|
|
2383
|
+
count: number,
|
|
2384
|
+
): void {
|
|
2385
|
+
throw new Error("Method not implemented.");
|
|
2386
|
+
}
|
|
1642
2387
|
|
|
1643
|
-
|
|
2388
|
+
private get table(): ComposeTable {
|
|
2389
|
+
return this.crossFieldTable as ComposeTable;
|
|
2390
|
+
}
|
|
1644
2391
|
}
|
|
1645
2392
|
|
|
1646
2393
|
function makeModularChangeset(
|
|
1647
2394
|
fieldChanges: FieldChangeMap | undefined = undefined,
|
|
1648
2395
|
nodeChanges: ChangeAtomIdMap<NodeChangeset> | undefined = undefined,
|
|
2396
|
+
nodeToParent: ChangeAtomIdMap<FieldId> | undefined = undefined,
|
|
2397
|
+
nodeAliases: ChangeAtomIdMap<NodeId> | undefined = undefined,
|
|
2398
|
+
crossFieldKeys: CrossFieldKeyTable | undefined = undefined,
|
|
1649
2399
|
maxId: number = -1,
|
|
1650
2400
|
revisions: readonly RevisionInfo[] | undefined = undefined,
|
|
1651
2401
|
constraintViolationCount: number | undefined = undefined,
|
|
@@ -1656,7 +2406,11 @@ function makeModularChangeset(
|
|
|
1656
2406
|
const changeset: Mutable<ModularChangeset> = {
|
|
1657
2407
|
fieldChanges: fieldChanges ?? new Map(),
|
|
1658
2408
|
nodeChanges: nodeChanges ?? new Map(),
|
|
2409
|
+
nodeToParent: nodeToParent ?? new Map(),
|
|
2410
|
+
nodeAliases: nodeAliases ?? new Map(),
|
|
2411
|
+
crossFieldKeys: crossFieldKeys ?? newCrossFieldKeyTable(),
|
|
1659
2412
|
};
|
|
2413
|
+
|
|
1660
2414
|
if (revisions !== undefined && revisions.length > 0) {
|
|
1661
2415
|
changeset.revisions = revisions;
|
|
1662
2416
|
}
|
|
@@ -1684,6 +2438,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
1684
2438
|
|
|
1685
2439
|
public constructor(
|
|
1686
2440
|
family: ChangeFamily<ChangeFamilyEditor, ModularChangeset>,
|
|
2441
|
+
private readonly fieldKinds: ReadonlyMap<FieldKindIdentifier, FieldKindWithEditor>,
|
|
1687
2442
|
changeReceiver: (change: ModularChangeset) => void,
|
|
1688
2443
|
) {
|
|
1689
2444
|
super(family, changeReceiver);
|
|
@@ -1744,11 +2499,18 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
1744
2499
|
fieldKind: FieldKindIdentifier,
|
|
1745
2500
|
change: FieldChangeset,
|
|
1746
2501
|
): void {
|
|
2502
|
+
const crossFieldKeys = getChangeHandler(this.fieldKinds, fieldKind).getCrossFieldKeys(
|
|
2503
|
+
change,
|
|
2504
|
+
);
|
|
2505
|
+
|
|
1747
2506
|
const modularChange = buildModularChangesetFromField(
|
|
1748
2507
|
field,
|
|
1749
2508
|
{ fieldKind, change },
|
|
1750
2509
|
new Map(),
|
|
2510
|
+
new Map(),
|
|
2511
|
+
newCrossFieldKeyTable(),
|
|
1751
2512
|
this.idAllocator,
|
|
2513
|
+
crossFieldKeys,
|
|
1752
2514
|
);
|
|
1753
2515
|
this.applyChange(modularChange);
|
|
1754
2516
|
}
|
|
@@ -1763,6 +2525,9 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
1763
2525
|
makeAnonChange(
|
|
1764
2526
|
change.type === "global"
|
|
1765
2527
|
? makeModularChangeset(
|
|
2528
|
+
undefined,
|
|
2529
|
+
undefined,
|
|
2530
|
+
undefined,
|
|
1766
2531
|
undefined,
|
|
1767
2532
|
undefined,
|
|
1768
2533
|
this.idAllocator.getMaxId(),
|
|
@@ -1777,7 +2542,12 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
1777
2542
|
change: change.change,
|
|
1778
2543
|
},
|
|
1779
2544
|
new Map(),
|
|
2545
|
+
new Map(),
|
|
2546
|
+
newCrossFieldKeyTable(),
|
|
1780
2547
|
this.idAllocator,
|
|
2548
|
+
getChangeHandler(this.fieldKinds, change.fieldKind).getCrossFieldKeys(
|
|
2549
|
+
change.change,
|
|
2550
|
+
),
|
|
1781
2551
|
),
|
|
1782
2552
|
),
|
|
1783
2553
|
);
|
|
@@ -1801,7 +2571,14 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
|
|
|
1801
2571
|
};
|
|
1802
2572
|
|
|
1803
2573
|
this.applyChange(
|
|
1804
|
-
buildModularChangesetFromNode(
|
|
2574
|
+
buildModularChangesetFromNode(
|
|
2575
|
+
path,
|
|
2576
|
+
nodeChange,
|
|
2577
|
+
new Map(),
|
|
2578
|
+
new Map(),
|
|
2579
|
+
newCrossFieldKeyTable(),
|
|
2580
|
+
this.idAllocator,
|
|
2581
|
+
),
|
|
1805
2582
|
);
|
|
1806
2583
|
}
|
|
1807
2584
|
}
|
|
@@ -1810,29 +2587,74 @@ function buildModularChangesetFromField(
|
|
|
1810
2587
|
path: FieldUpPath,
|
|
1811
2588
|
fieldChange: FieldChange,
|
|
1812
2589
|
nodeChanges: ChangeAtomIdMap<NodeChangeset>,
|
|
2590
|
+
nodeToParent: ChangeAtomIdMap<FieldId>,
|
|
2591
|
+
crossFieldKeys: CrossFieldKeyTable,
|
|
1813
2592
|
idAllocator: IdAllocator = idAllocatorFromMaxId(),
|
|
2593
|
+
localCrossFieldKeys: CrossFieldKeyRange[] = [],
|
|
2594
|
+
childId: NodeId | undefined = undefined,
|
|
1814
2595
|
): ModularChangeset {
|
|
1815
2596
|
const fieldChanges: FieldChangeMap = new Map([[path.field, fieldChange]]);
|
|
1816
2597
|
|
|
1817
2598
|
if (path.parent === undefined) {
|
|
1818
|
-
|
|
2599
|
+
for (const key of localCrossFieldKeys) {
|
|
2600
|
+
crossFieldKeys.set(key, { nodeId: undefined, field: path.field });
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
if (childId !== undefined) {
|
|
2604
|
+
setInChangeAtomIdMap(nodeToParent, childId, {
|
|
2605
|
+
nodeId: undefined,
|
|
2606
|
+
field: path.field,
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
return makeModularChangeset(
|
|
2611
|
+
fieldChanges,
|
|
2612
|
+
nodeChanges,
|
|
2613
|
+
nodeToParent,
|
|
2614
|
+
undefined,
|
|
2615
|
+
crossFieldKeys,
|
|
2616
|
+
idAllocator.getMaxId(),
|
|
2617
|
+
);
|
|
1819
2618
|
}
|
|
1820
2619
|
|
|
1821
2620
|
const nodeChangeset: NodeChangeset = {
|
|
1822
2621
|
fieldChanges,
|
|
1823
2622
|
};
|
|
1824
2623
|
|
|
1825
|
-
|
|
2624
|
+
const parentId: NodeId = { localId: brand(idAllocator.allocate()) };
|
|
2625
|
+
|
|
2626
|
+
for (const key of localCrossFieldKeys) {
|
|
2627
|
+
crossFieldKeys.set(key, { nodeId: parentId, field: path.field });
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
if (childId !== undefined) {
|
|
2631
|
+
setInChangeAtomIdMap(nodeToParent, childId, {
|
|
2632
|
+
nodeId: parentId,
|
|
2633
|
+
field: path.field,
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
return buildModularChangesetFromNode(
|
|
2638
|
+
path.parent,
|
|
2639
|
+
nodeChangeset,
|
|
2640
|
+
nodeChanges,
|
|
2641
|
+
nodeToParent,
|
|
2642
|
+
crossFieldKeys,
|
|
2643
|
+
idAllocator,
|
|
2644
|
+
parentId,
|
|
2645
|
+
);
|
|
1826
2646
|
}
|
|
1827
2647
|
|
|
1828
2648
|
function buildModularChangesetFromNode(
|
|
1829
2649
|
path: UpPath,
|
|
1830
2650
|
nodeChange: NodeChangeset,
|
|
1831
2651
|
nodeChanges: ChangeAtomIdMap<NodeChangeset>,
|
|
2652
|
+
nodeToParent: ChangeAtomIdMap<FieldId>,
|
|
2653
|
+
crossFieldKeys: CrossFieldKeyTable,
|
|
1832
2654
|
idAllocator: IdAllocator,
|
|
2655
|
+
nodeId: NodeId = { localId: brand(idAllocator.allocate()) },
|
|
1833
2656
|
): ModularChangeset {
|
|
1834
|
-
|
|
1835
|
-
setInNestedMap(nodeChanges, nodeId.revision, nodeId.localId, nodeChange);
|
|
2657
|
+
setInChangeAtomIdMap(nodeChanges, nodeId, nodeChange);
|
|
1836
2658
|
const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChange(
|
|
1837
2659
|
path.parentIndex,
|
|
1838
2660
|
nodeId,
|
|
@@ -1847,7 +2669,11 @@ function buildModularChangesetFromNode(
|
|
|
1847
2669
|
{ parent: path.parent, field: path.parentField },
|
|
1848
2670
|
fieldChange,
|
|
1849
2671
|
nodeChanges,
|
|
2672
|
+
nodeToParent,
|
|
2673
|
+
crossFieldKeys,
|
|
1850
2674
|
idAllocator,
|
|
2675
|
+
[],
|
|
2676
|
+
nodeId,
|
|
1851
2677
|
);
|
|
1852
2678
|
}
|
|
1853
2679
|
|
|
@@ -1936,8 +2762,259 @@ function revisionFromRevInfos(
|
|
|
1936
2762
|
return revInfos[0].revision;
|
|
1937
2763
|
}
|
|
1938
2764
|
|
|
1939
|
-
function
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2765
|
+
function mergeBTrees<K, V>(tree1: BTree<K, V>, tree2: BTree<K, V>): BTree<K, V> {
|
|
2766
|
+
const result = tree1.clone();
|
|
2767
|
+
tree2.forEachPair((k, v) => {
|
|
2768
|
+
result.set(k, v);
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2771
|
+
return result;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
function mergeNestedMaps<K1, K2, V>(
|
|
2775
|
+
map1: NestedMap<K1, K2, V>,
|
|
2776
|
+
map2: NestedMap<K1, K2, V>,
|
|
2777
|
+
): NestedMap<K1, K2, V> {
|
|
2778
|
+
const merged: NestedMap<K1, K2, V> = new Map();
|
|
2779
|
+
populateNestedMap(map1, merged, true);
|
|
2780
|
+
populateNestedMap(map2, merged, true);
|
|
2781
|
+
return merged;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
function fieldChangeFromId(
|
|
2785
|
+
fields: FieldChangeMap,
|
|
2786
|
+
nodes: ChangeAtomIdMap<NodeChangeset>,
|
|
2787
|
+
id: FieldId,
|
|
2788
|
+
): FieldChange {
|
|
2789
|
+
const fieldMap = fieldMapFromNodeId(fields, nodes, id.nodeId);
|
|
2790
|
+
return fieldMap.get(id.field) ?? fail("No field exists for the given ID");
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
function fieldMapFromNodeId(
|
|
2794
|
+
rootFieldMap: FieldChangeMap,
|
|
2795
|
+
nodes: ChangeAtomIdMap<NodeChangeset>,
|
|
2796
|
+
nodeId: NodeId | undefined,
|
|
2797
|
+
): FieldChangeMap {
|
|
2798
|
+
if (nodeId === undefined) {
|
|
2799
|
+
return rootFieldMap;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
const node = nodeChangeFromId(nodes, nodeId);
|
|
2803
|
+
assert(node.fieldChanges !== undefined, 0x9c9 /* Expected node to have field changes */);
|
|
2804
|
+
return node.fieldChanges;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
function rebasedFieldIdFromBaseId(table: RebaseTable, baseId: FieldId): FieldId {
|
|
2808
|
+
if (baseId.nodeId === undefined) {
|
|
2809
|
+
return baseId;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
return { ...baseId, nodeId: rebasedNodeIdFromBaseNodeId(table, baseId.nodeId) };
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
function rebasedNodeIdFromBaseNodeId(table: RebaseTable, baseId: NodeId): NodeId {
|
|
2816
|
+
return getFromChangeAtomIdMap(table.baseToRebasedNodeId, baseId) ?? baseId;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
function nodeChangeFromId(nodes: ChangeAtomIdMap<NodeChangeset>, id: NodeId): NodeChangeset {
|
|
2820
|
+
const node = getFromChangeAtomIdMap(nodes, id);
|
|
2821
|
+
assert(node !== undefined, 0x9ca /* Unknown node ID */);
|
|
2822
|
+
return node;
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
function fieldIdFromFieldIdKey([revision, localId, field]: FieldIdKey): FieldId {
|
|
2826
|
+
const nodeId = localId !== undefined ? { revision, localId } : undefined;
|
|
2827
|
+
return { nodeId, field };
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
function cloneNodeChangeset(nodeChangeset: NodeChangeset): NodeChangeset {
|
|
2831
|
+
if (nodeChangeset.fieldChanges !== undefined) {
|
|
2832
|
+
return { ...nodeChangeset, fieldChanges: new Map(nodeChangeset.fieldChanges) };
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
return { ...nodeChangeset };
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
function replaceFieldIdRevision(
|
|
2839
|
+
fieldId: FieldId,
|
|
2840
|
+
oldRevisions: Set<RevisionTag | undefined>,
|
|
2841
|
+
newRevision: RevisionTag | undefined,
|
|
2842
|
+
): FieldId {
|
|
2843
|
+
if (fieldId.nodeId === undefined) {
|
|
2844
|
+
return fieldId;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
return {
|
|
2848
|
+
...fieldId,
|
|
2849
|
+
nodeId: replaceAtomRevisions(fieldId.nodeId, oldRevisions, newRevision),
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
export function getParentFieldId(changeset: ModularChangeset, nodeId: NodeId): FieldId {
|
|
2854
|
+
const parentId = getFromChangeAtomIdMap(changeset.nodeToParent, nodeId);
|
|
2855
|
+
assert(parentId !== undefined, 0x9cb /* Parent field should be defined */);
|
|
2856
|
+
return normalizeFieldId(parentId, changeset.nodeAliases);
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
export function getFieldsForCrossFieldKey(
|
|
2860
|
+
changeset: ModularChangeset,
|
|
2861
|
+
[target, revision, id, count]: CrossFieldKeyRange,
|
|
2862
|
+
): FieldId[] {
|
|
2863
|
+
let firstLocalId: number = id;
|
|
2864
|
+
const lastLocalId = id + count - 1;
|
|
2865
|
+
|
|
2866
|
+
const fields: FieldId[] = [];
|
|
2867
|
+
|
|
2868
|
+
// eslint-disable-next-line no-constant-condition
|
|
2869
|
+
while (true) {
|
|
2870
|
+
const entry = getFirstIntersectingCrossFieldEntry(changeset.crossFieldKeys, [
|
|
2871
|
+
target,
|
|
2872
|
+
revision,
|
|
2873
|
+
brand(firstLocalId),
|
|
2874
|
+
count,
|
|
2875
|
+
]);
|
|
2876
|
+
|
|
2877
|
+
if (entry === undefined) {
|
|
2878
|
+
return fields;
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
const [[_target, _revision, entryId, entryCount], fieldId] = entry;
|
|
2882
|
+
fields.push(normalizeFieldId(fieldId, changeset.nodeAliases));
|
|
2883
|
+
|
|
2884
|
+
const entryLastId = entryId + entryCount - 1;
|
|
2885
|
+
if (entryLastId >= lastLocalId) {
|
|
2886
|
+
return fields;
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
firstLocalId = entryLastId + 1;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
function getFirstIntersectingCrossFieldEntry(
|
|
2894
|
+
table: CrossFieldKeyTable,
|
|
2895
|
+
[target, revision, id, count]: CrossFieldKeyRange,
|
|
2896
|
+
): [CrossFieldKeyRange, FieldId] | undefined {
|
|
2897
|
+
const entry = table.nextLowerPair([target, revision, id, Infinity]);
|
|
2898
|
+
if (entry === undefined) {
|
|
2899
|
+
return undefined;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
const [entryTarget, entryRevision, entryId, entryCount] = entry[0];
|
|
2903
|
+
if (entryTarget !== target || entryRevision !== revision) {
|
|
2904
|
+
return undefined;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
const lastQueryId = id + count - 1;
|
|
2908
|
+
const entryLastId = entryId + entryCount - 1;
|
|
2909
|
+
if (entryId > lastQueryId || entryLastId < id) {
|
|
2910
|
+
return undefined;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
return entry;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
function setInCrossFieldKeyTable(
|
|
2917
|
+
table: CrossFieldKeyTable,
|
|
2918
|
+
target: CrossFieldTarget,
|
|
2919
|
+
revision: RevisionTag | undefined,
|
|
2920
|
+
id: ChangesetLocalId,
|
|
2921
|
+
count: number,
|
|
2922
|
+
value: FieldId,
|
|
2923
|
+
): void {
|
|
2924
|
+
let entry = getFirstIntersectingCrossFieldEntry(table, [target, revision, id, count]);
|
|
2925
|
+
const lastQueryId = id + count - 1;
|
|
2926
|
+
while (entry !== undefined) {
|
|
2927
|
+
const [entryKey, entryValue] = entry;
|
|
2928
|
+
table.delete(entryKey);
|
|
2929
|
+
|
|
2930
|
+
const [_target, _revision, entryId, entryCount] = entryKey;
|
|
2931
|
+
if (entryId < id) {
|
|
2932
|
+
table.set([target, revision, entryId, id - entryId], entryValue);
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2935
|
+
const lastEntryId = entryId + entryCount - 1;
|
|
2936
|
+
if (lastEntryId > lastQueryId) {
|
|
2937
|
+
table.set(
|
|
2938
|
+
[target, revision, brand(lastQueryId + 1), lastEntryId - lastQueryId],
|
|
2939
|
+
entryValue,
|
|
2940
|
+
);
|
|
2941
|
+
break;
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
const nextId: ChangesetLocalId = brand(lastEntryId + 1);
|
|
2945
|
+
entry = getFirstIntersectingCrossFieldEntry(table, [
|
|
2946
|
+
target,
|
|
2947
|
+
revision,
|
|
2948
|
+
nextId,
|
|
2949
|
+
lastQueryId - nextId + 1,
|
|
2950
|
+
]);
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
table.set([target, revision, id, count], value);
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
function normalizeFieldId(fieldId: FieldId, nodeAliases: ChangeAtomIdMap<NodeId>): FieldId {
|
|
2957
|
+
return fieldId.nodeId !== undefined
|
|
2958
|
+
? { ...fieldId, nodeId: normalizeNodeId(fieldId.nodeId, nodeAliases) }
|
|
2959
|
+
: fieldId;
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
/**
|
|
2963
|
+
* @returns The canonical form of nodeId, according to nodeAliases
|
|
2964
|
+
*/
|
|
2965
|
+
function normalizeNodeId(nodeId: NodeId, nodeAliases: ChangeAtomIdMap<NodeId>): NodeId {
|
|
2966
|
+
let currentId = nodeId;
|
|
2967
|
+
|
|
2968
|
+
// eslint-disable-next-line no-constant-condition
|
|
2969
|
+
while (true) {
|
|
2970
|
+
const dealiased = getFromChangeAtomIdMap(nodeAliases, currentId);
|
|
2971
|
+
if (dealiased === undefined) {
|
|
2972
|
+
return currentId;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
currentId = dealiased;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
function hasConflicts(change: ModularChangeset): boolean {
|
|
2980
|
+
return (change.constraintViolationCount ?? 0) > 0;
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
export function newCrossFieldKeyTable(): CrossFieldKeyTable {
|
|
2984
|
+
return newBTree();
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
function newBTree<K extends readonly unknown[], V>(): TupleBTree<K, V> {
|
|
2988
|
+
return brand(new BTree<K, V>(undefined, compareTuples));
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// This assumes that the arrays are the same length.
|
|
2992
|
+
function compareTuples(arrayA: readonly unknown[], arrayB: readonly unknown[]): number {
|
|
2993
|
+
for (let i = 0; i < arrayA.length; i++) {
|
|
2994
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2995
|
+
const a = arrayA[i] as any;
|
|
2996
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2997
|
+
const b = arrayB[i] as any;
|
|
2998
|
+
if (a < b) {
|
|
2999
|
+
return -1;
|
|
3000
|
+
} else if (a > b) {
|
|
3001
|
+
return 1;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
return 0;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
interface ModularChangesetContent {
|
|
3009
|
+
fieldChanges: FieldChangeMap;
|
|
3010
|
+
nodeChanges: ChangeAtomIdMap<NodeChangeset>;
|
|
3011
|
+
nodeToParent: ChangeAtomIdMap<FieldId>;
|
|
3012
|
+
nodeAliases: ChangeAtomIdMap<NodeId>;
|
|
3013
|
+
crossFieldKeys: CrossFieldKeyTable;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
function cloneNestedMap<K1, K2, V>(map: NestedMap<K1, K2, V>): NestedMap<K1, K2, V> {
|
|
3017
|
+
const cloned: NestedMap<K1, K2, V> = new Map();
|
|
3018
|
+
populateNestedMap(map, cloned, true);
|
|
3019
|
+
return cloned;
|
|
1943
3020
|
}
|