@@ -306,7 +306,7 @@ export function transformSets( operationsA, operationsB, options ) {
306306 originalOperationsBCount : operationsB . length
307307 } ;
308308
309- const contextFactory = new ContextFactory ( options . document , options . useRelations ) ;
309+ const contextFactory = new ContextFactory ( options . document , options . useRelations , options . forceWeakRemove ) ;
310310 contextFactory . setOriginalOperations ( operationsA ) ;
311311 contextFactory . setOriginalOperations ( operationsB ) ;
312312
@@ -386,13 +386,17 @@ class ContextFactory {
386386 // @param {module:engine/model/document~Document } document Document which the operations change.
387387 // @param {Boolean } useRelations Whether during transformation relations should be used (used during undo for
388388 // better conflict resolution).
389- constructor ( document , useRelations ) {
389+ // @param {Boolean } [forceWeakRemove=false] If set to `false`, remove operation will be always stronger than move operation,
390+ // so the removed nodes won't end up back in the document root. When set to `true`, context data will be used.
391+ constructor ( document , useRelations , forceWeakRemove = false ) {
390392 // `model.History` instance which information about undone operations will be taken from.
391393 this . _history = document . history ;
392394
393395 // Whether additional context should be used.
394396 this . _useRelations = useRelations ;
395397
398+ this . _forceWeakRemove = ! ! forceWeakRemove ;
399+
396400 // For each operation that is created during transformation process, we keep a reference to the original operation
397401 // which it comes from. The original operation works as a kind of "identifier". Every contextual information
398402 // gathered during transformation that we want to save for given operation, is actually saved for the original operation.
@@ -583,7 +587,8 @@ class ContextFactory {
583587 aWasUndone : this . _wasUndone ( opA ) ,
584588 bWasUndone : this . _wasUndone ( opB ) ,
585589 abRelation : this . _useRelations ? this . _getRelation ( opA , opB ) : null ,
586- baRelation : this . _useRelations ? this . _getRelation ( opB , opA ) : null
590+ baRelation : this . _useRelations ? this . _getRelation ( opB , opA ) : null ,
591+ forceWeakRemove : this . _forceWeakRemove
587592 } ;
588593 }
589594
@@ -1313,7 +1318,7 @@ setTransformation( MergeOperation, MoveOperation, ( a, b, context ) => {
13131318 //
13141319 const removedRange = Range . _createFromPositionAndShift ( b . sourcePosition , b . howMany ) ;
13151320
1316- if ( b . type == 'remove' && ! context . bWasUndone ) {
1321+ if ( b . type == 'remove' && ! context . bWasUndone && ! context . forceWeakRemove ) {
13171322 if ( a . deletionPosition . hasSameParentAs ( b . sourcePosition ) && removedRange . containsPosition ( a . sourcePosition ) ) {
13181323 return [ new NoOperation ( 0 ) ] ;
13191324 }
@@ -1596,9 +1601,9 @@ setTransformation( MoveOperation, MoveOperation, ( a, b, context ) => {
15961601 //
15971602 // If only one of operations is a remove operation, we force remove operation to be the "stronger" one
15981603 // to provide more expected results.
1599- if ( a . type == 'remove' && b . type != 'remove' && ! context . aWasUndone ) {
1604+ if ( a . type == 'remove' && b . type != 'remove' && ! context . aWasUndone && ! context . forceWeakRemove ) {
16001605 aIsStrong = true ;
1601- } else if ( a . type != 'remove' && b . type == 'remove' && ! context . bWasUndone ) {
1606+ } else if ( a . type != 'remove' && b . type == 'remove' && ! context . bWasUndone && ! context . forceWeakRemove ) {
16021607 aIsStrong = false ;
16031608 }
16041609
@@ -1768,7 +1773,7 @@ setTransformation( MoveOperation, SplitOperation, ( a, b, context ) => {
17681773 if ( b . graveyardPosition ) {
17691774 const movesGraveyardElement = moveRange . start . isEqual ( b . graveyardPosition ) || moveRange . containsPosition ( b . graveyardPosition ) ;
17701775
1771- if ( a . howMany > 1 && movesGraveyardElement ) {
1776+ if ( a . howMany > 1 && movesGraveyardElement && ! context . aWasUndone ) {
17721777 ranges . push ( Range . _createFromPositionAndShift ( b . insertionPosition , 1 ) ) ;
17731778 }
17741779 }
@@ -1780,7 +1785,7 @@ setTransformation( MoveOperation, MergeOperation, ( a, b, context ) => {
17801785 const movedRange = Range . _createFromPositionAndShift ( a . sourcePosition , a . howMany ) ;
17811786
17821787 if ( b . deletionPosition . hasSameParentAs ( a . sourcePosition ) && movedRange . containsPosition ( b . sourcePosition ) ) {
1783- if ( a . type == 'remove' ) {
1788+ if ( a . type == 'remove' && ! context . forceWeakRemove ) {
17841789 // Case 1:
17851790 //
17861791 // The element to remove got merged.
@@ -1794,21 +1799,22 @@ setTransformation( MoveOperation, MergeOperation, ( a, b, context ) => {
17941799 const results = [ ] ;
17951800
17961801 let gyMoveSource = b . graveyardPosition . clone ( ) ;
1797- let splitNodesMoveSource = b . targetPosition . clone ( ) ;
1802+ let splitNodesMoveSource = b . targetPosition . _getTransformedByMergeOperation ( b ) ;
17981803
17991804 if ( a . howMany > 1 ) {
18001805 results . push ( new MoveOperation ( a . sourcePosition , a . howMany - 1 , a . targetPosition , 0 ) ) ;
1801- gyMoveSource = gyMoveSource . _getTransformedByInsertion ( a . targetPosition , a . howMany - 1 ) ;
1806+
1807+ gyMoveSource = gyMoveSource . _getTransformedByMove ( a . sourcePosition , a . targetPosition , a . howMany - 1 ) ;
18021808 splitNodesMoveSource = splitNodesMoveSource . _getTransformedByMove ( a . sourcePosition , a . targetPosition , a . howMany - 1 ) ;
18031809 }
18041810
18051811 const gyMoveTarget = b . deletionPosition . _getCombined ( a . sourcePosition , a . targetPosition ) ;
18061812 const gyMove = new MoveOperation ( gyMoveSource , 1 , gyMoveTarget , 0 ) ;
18071813
1808- const targetPositionPath = gyMove . getMovedRangeStart ( ) . path . slice ( ) ;
1809- targetPositionPath . push ( 0 ) ;
1814+ const splitNodesMoveTargetPath = gyMove . getMovedRangeStart ( ) . path . slice ( ) ;
1815+ splitNodesMoveTargetPath . push ( 0 ) ;
18101816
1811- const splitNodesMoveTarget = new Position ( gyMove . targetPosition . root , targetPositionPath ) ;
1817+ const splitNodesMoveTarget = new Position ( gyMove . targetPosition . root , splitNodesMoveTargetPath ) ;
18121818 splitNodesMoveSource = splitNodesMoveSource . _getTransformedByMove ( gyMoveSource , gyMoveTarget , 1 ) ;
18131819 const splitNodesMove = new MoveOperation ( splitNodesMoveSource , b . howMany , splitNodesMoveTarget , 0 ) ;
18141820
@@ -2052,7 +2058,9 @@ setTransformation( SplitOperation, MoveOperation, ( a, b, context ) => {
20522058 // is already moved to the correct position, we need to only move the nodes after the split position.
20532059 // This will be done by `MoveOperation` instead of `SplitOperation`.
20542060 //
2055- if ( rangeToMove . start . isEqual ( a . graveyardPosition ) || rangeToMove . containsPosition ( a . graveyardPosition ) ) {
2061+ const gyElementMoved = rangeToMove . start . isEqual ( a . graveyardPosition ) || rangeToMove . containsPosition ( a . graveyardPosition ) ;
2062+
2063+ if ( ! context . bWasUndone && gyElementMoved ) {
20562064 const sourcePosition = a . splitPosition . _getTransformedByMoveOperation ( b ) ;
20572065
20582066 const newParentPosition = a . graveyardPosition . _getTransformedByMoveOperation ( b ) ;
0 commit comments