|
@@ -598,40 +598,6 @@ function updateRelations( opA, opB, context ) {
|
|
|
|
|
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- case InsertOperation: {
|
|
|
|
|
- switch ( opB.constructor ) {
|
|
|
|
|
- case MergeOperation: {
|
|
|
|
|
- if ( opA.position.isEqual( opB.sourcePosition ) || opB.movedRange.containsPosition( opA.position ) ) {
|
|
|
|
|
- setRelation( context, opA, opB, 'insertAtSource' );
|
|
|
|
|
- } else if ( opA.position.isEqual( opB.deletionPosition ) ) {
|
|
|
|
|
- setRelation( context, opA, opB, 'insertBetween' );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case MoveOperation: {
|
|
|
|
|
- if ( opA.position.isEqual( opB.sourcePosition ) || opA.position.isBefore( opB.sourcePosition ) ) {
|
|
|
|
|
- setRelation( context, opA, opB, 'insertBefore' );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case UnwrapOperation: {
|
|
|
|
|
- const isInside = opA.position.hasSameParentAs( opB.position );
|
|
|
|
|
-
|
|
|
|
|
- if ( isInside ) {
|
|
|
|
|
- setRelation( context, opA, opB, 'insertInside' );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1110,29 +1076,7 @@ setTransformation( InsertOperation, InsertOperation, ( a, b, context ) => {
|
|
|
return [ a ];
|
|
return [ a ];
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
-setTransformation( InsertOperation, MoveOperation, ( a, b, context ) => {
|
|
|
|
|
- // Case 1:
|
|
|
|
|
- //
|
|
|
|
|
- // Operations insert nodes at the same position, similarly as above. This time these are different operations,
|
|
|
|
|
- // so we can just decide that either insert or move will always be before and the other will be always after.
|
|
|
|
|
- // And we decide that that `InsertOperation` will be stronger -- so if operations have equal insertion positions,
|
|
|
|
|
- // insert operation position will not be transformed.
|
|
|
|
|
- //
|
|
|
|
|
- // However, in case of move operation, we might have additional contextual information to help in
|
|
|
|
|
- // resolving this conflict. Move operation might be a result of undo. Because of that, we need to check if
|
|
|
|
|
- // there is a relation between the operations.
|
|
|
|
|
- //
|
|
|
|
|
- // This relation checks if `b` is undoing operation, if so, it looks at the undone operation. That undone
|
|
|
|
|
- // operation from the past is also a move operation, but the source and target position are flipped. This
|
|
|
|
|
- // enables us to check if the insert operation targeted before the moved nodes or after. This gives us
|
|
|
|
|
- // context on what to do now.
|
|
|
|
|
- //
|
|
|
|
|
- // Of course the relations are evaluated and saved earlier, now they are only read and used.
|
|
|
|
|
- //
|
|
|
|
|
- if ( a.position.isEqual( b.targetPosition ) && context.abRelation == 'insertBefore' ) {
|
|
|
|
|
- return [ a ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+setTransformation( InsertOperation, MoveOperation, ( a, b ) => {
|
|
|
// The default case.
|
|
// The default case.
|
|
|
//
|
|
//
|
|
|
a.position = a.position._getTransformedByMoveOperation( b );
|
|
a.position = a.position._getTransformedByMoveOperation( b );
|
|
@@ -1140,18 +1084,7 @@ setTransformation( InsertOperation, MoveOperation, ( a, b, context ) => {
|
|
|
return [ a ];
|
|
return [ a ];
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
-setTransformation( InsertOperation, SplitOperation, ( a, b, context ) => {
|
|
|
|
|
- // Case 1:
|
|
|
|
|
- //
|
|
|
|
|
- // Insert puts nodes at the split position. We need to decide if the nodes should be inserted at the end of
|
|
|
|
|
- // the split element or at the beginning of the new element.
|
|
|
|
|
- //
|
|
|
|
|
- if ( a.position.isEqual( b.position ) && context.abRelation == 'insertAtSource' ) {
|
|
|
|
|
- a.position = b.moveTargetPosition;
|
|
|
|
|
-
|
|
|
|
|
- return [ a ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+setTransformation( InsertOperation, SplitOperation, ( a, b ) => {
|
|
|
// The default case.
|
|
// The default case.
|
|
|
//
|
|
//
|
|
|
a.position = a.position._getTransformedBySplitOperation( b );
|
|
a.position = a.position._getTransformedBySplitOperation( b );
|
|
@@ -1165,45 +1098,7 @@ setTransformation( InsertOperation, MergeOperation, ( a, b ) => {
|
|
|
return [ a ];
|
|
return [ a ];
|
|
|
} );
|
|
} );
|
|
|
|
|
|
|
|
-setTransformation( InsertOperation, WrapOperation, ( a, b, context ) => {
|
|
|
|
|
- // Case 1:
|
|
|
|
|
- //
|
|
|
|
|
- // Insert puts nodes at the beginning of wrapped range. Normally, we don't want the inserted nodes to be
|
|
|
|
|
- // accidentally wrapped. However, if the wrap operation comes from undo, and it undoes an unwrap operation
|
|
|
|
|
- // and the insert actually was putting nodes inside the unwrapped element, then we need the insert operation
|
|
|
|
|
- // to put nodes back to the once-unwrapped element.
|
|
|
|
|
- //
|
|
|
|
|
- // Example. There is a block quote with a paragraph and new paragraph is inserted at `^`. Before that,
|
|
|
|
|
- // block quote is unwrapped, and then this is undone:
|
|
|
|
|
- //
|
|
|
|
|
- // <blockQuote>^<p>Foo</p></blockQuote>
|
|
|
|
|
- // ^<p>Foo</p>
|
|
|
|
|
- //
|
|
|
|
|
- // This transformation and this conflict is shown before. `<p>Foo</p>` needs to be wrapped, but should the
|
|
|
|
|
- // inserted nodes be "included" in the wrap or not?
|
|
|
|
|
- //
|
|
|
|
|
- // [^<p>Foo</p>] or ^[<p>Foo</p>]?
|
|
|
|
|
- //
|
|
|
|
|
- // According to the state at the beginning, the intention was to insert nodes inside the `<blockQuote>`,
|
|
|
|
|
- // so we should take that into consideration and put the inserted nodes into the wrapped node.
|
|
|
|
|
- //
|
|
|
|
|
- // In contrary, this is an alternative scenario:
|
|
|
|
|
- //
|
|
|
|
|
- // ^<blockQuote><p>Foo</p></blockQuote>
|
|
|
|
|
- // ^<p>Foo</p>
|
|
|
|
|
- //
|
|
|
|
|
- // As can be seen, different scenario led to the same situation and the same question:
|
|
|
|
|
- //
|
|
|
|
|
- // [^<p>Foo</p>] or ^[<p>Foo</p>]?
|
|
|
|
|
- //
|
|
|
|
|
- // However this time we should not include the inserted nodes into the wrapped element.
|
|
|
|
|
- //
|
|
|
|
|
- if ( a.position.isEqual( b.position ) && context.abRelation == 'insertInside' ) {
|
|
|
|
|
- a.position = b.targetPosition;
|
|
|
|
|
-
|
|
|
|
|
- return [ a ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+setTransformation( InsertOperation, WrapOperation, ( a, b ) => {
|
|
|
// The default case.
|
|
// The default case.
|
|
|
//
|
|
//
|
|
|
a.position = a.position._getTransformedByWrapOperation( b );
|
|
a.position = a.position._getTransformedByWrapOperation( b );
|
|
@@ -1591,7 +1486,7 @@ setTransformation( MergeOperation, UnwrapOperation, ( a, b, context ) => {
|
|
|
|
|
|
|
|
// -----------------------
|
|
// -----------------------
|
|
|
|
|
|
|
|
-setTransformation( MoveOperation, InsertOperation, ( a, b, context ) => {
|
|
|
|
|
|
|
+setTransformation( MoveOperation, InsertOperation, ( a, b ) => {
|
|
|
const moveRange = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
|
|
const moveRange = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
|
|
|
const transformed = moveRange._getTransformedByInsertOperation( b, false )[ 0 ];
|
|
const transformed = moveRange._getTransformedByInsertOperation( b, false )[ 0 ];
|
|
|
|
|
|
|
@@ -1604,7 +1499,7 @@ setTransformation( MoveOperation, InsertOperation, ( a, b, context ) => {
|
|
|
// `MoveOperation` is considered weaker, so it is always transformed, unless there was a certain relation
|
|
// `MoveOperation` is considered weaker, so it is always transformed, unless there was a certain relation
|
|
|
// between operations.
|
|
// between operations.
|
|
|
//
|
|
//
|
|
|
- if ( !a.targetPosition.isEqual( b.position ) || context.abRelation == 'insertBefore' ) {
|
|
|
|
|
|
|
+ if ( !a.targetPosition.isEqual( b.position ) ) {
|
|
|
a.targetPosition = a.targetPosition._getTransformedByInsertOperation( b );
|
|
a.targetPosition = a.targetPosition._getTransformedByInsertOperation( b );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2127,25 +2022,7 @@ setTransformation( RootAttributeOperation, RootAttributeOperation, ( a, b, conte
|
|
|
|
|
|
|
|
// -----------------------
|
|
// -----------------------
|
|
|
|
|
|
|
|
-setTransformation( SplitOperation, InsertOperation, ( a, b, context ) => {
|
|
|
|
|
- // Case 1:
|
|
|
|
|
- //
|
|
|
|
|
- // Split is at the position where nodes were inserted.
|
|
|
|
|
- //
|
|
|
|
|
- // This is a mirror situation to `InsertOperation` x `SplitOperation` handling of this case. We need to
|
|
|
|
|
- // decide if inserted nodes should be put into original node or into the new node. From the "split operation
|
|
|
|
|
- // point of view", it means that it either changes split position, or number of nodes to split.
|
|
|
|
|
- //
|
|
|
|
|
- // If there is relation and during merge operation from the past, the insert was at the source element,
|
|
|
|
|
- // we need to insert those nodes "after" the split position. So split position is not transformed,
|
|
|
|
|
- // only the number of nodes to move to the new element is changed.
|
|
|
|
|
- //
|
|
|
|
|
- if ( a.position.isEqual( b.position ) && context.baRelation == 'insertAtSource' ) {
|
|
|
|
|
- a.howMany += b.howMany;
|
|
|
|
|
-
|
|
|
|
|
- return [ a ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+setTransformation( SplitOperation, InsertOperation, ( a, b ) => {
|
|
|
// The default case.
|
|
// The default case.
|
|
|
//
|
|
//
|
|
|
if ( a.position.hasSameParentAs( b.position ) && a.position.offset < b.position.offset ) {
|
|
if ( a.position.hasSameParentAs( b.position ) && a.position.offset < b.position.offset ) {
|
|
@@ -2217,7 +2094,7 @@ setTransformation( SplitOperation, MoveOperation, ( a, b, context ) => {
|
|
|
// Split is at a position where nodes were moved.
|
|
// Split is at a position where nodes were moved.
|
|
|
//
|
|
//
|
|
|
// This is a scenario described in `MoveOperation` x `SplitOperation` transformation but from the
|
|
// This is a scenario described in `MoveOperation` x `SplitOperation` transformation but from the
|
|
|
- // "split operation point of view". See also `SplitOperation` x `InsertOperation` for similar case.
|
|
|
|
|
|
|
+ // "split operation point of view".
|
|
|
//
|
|
//
|
|
|
if ( a.position.isEqual( b.targetPosition ) && ( context.baRelation == 'insertAtSource' || context.abRelation == 'splitBefore' ) ) {
|
|
if ( a.position.isEqual( b.targetPosition ) && ( context.baRelation == 'insertAtSource' || context.abRelation == 'splitBefore' ) ) {
|
|
|
a.howMany += b.howMany;
|
|
a.howMany += b.howMany;
|
|
@@ -2397,18 +2274,7 @@ setTransformation( SplitOperation, UnwrapOperation, ( a, b, context ) => {
|
|
|
|
|
|
|
|
// -----------------------
|
|
// -----------------------
|
|
|
|
|
|
|
|
-setTransformation( WrapOperation, InsertOperation, ( a, b, context ) => {
|
|
|
|
|
- // Case 1:
|
|
|
|
|
- //
|
|
|
|
|
- // Insert position is at the beginning of the range to wrap. We need to decide if wrap operation should
|
|
|
|
|
- // also wrap those new nodes. See also `InsertOperation` x `WrapOperation`.
|
|
|
|
|
- //
|
|
|
|
|
- if ( a.position.isEqual( b.position ) && context.baRelation == 'insertInside' ) {
|
|
|
|
|
- a.howMany += b.howMany;
|
|
|
|
|
-
|
|
|
|
|
- return [ a ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+setTransformation( WrapOperation, InsertOperation, ( a, b ) => {
|
|
|
// The default case.
|
|
// The default case.
|
|
|
//
|
|
//
|
|
|
const transformed = a.wrappedRange._getTransformedByInsertOperation( b, false )[ 0 ];
|
|
const transformed = a.wrappedRange._getTransformedByInsertOperation( b, false )[ 0 ];
|