@@ -306,7 +306,7 @@ export function transformSets( operationsA, operationsB, options ) {
306
306
originalOperationsBCount : operationsB . length
307
307
} ;
308
308
309
- const contextFactory = new ContextFactory ( options . document , options . useRelations ) ;
309
+ const contextFactory = new ContextFactory ( options . document , options . useRelations , options . forceWeakRemove ) ;
310
310
contextFactory . setOriginalOperations ( operationsA ) ;
311
311
contextFactory . setOriginalOperations ( operationsB ) ;
312
312
@@ -386,13 +386,17 @@ class ContextFactory {
386
386
// @param {module:engine/model/document~Document } document Document which the operations change.
387
387
// @param {Boolean } useRelations Whether during transformation relations should be used (used during undo for
388
388
// 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 ) {
390
392
// `model.History` instance which information about undone operations will be taken from.
391
393
this . _history = document . history ;
392
394
393
395
// Whether additional context should be used.
394
396
this . _useRelations = useRelations ;
395
397
398
+ this . _forceWeakRemove = ! ! forceWeakRemove ;
399
+
396
400
// For each operation that is created during transformation process, we keep a reference to the original operation
397
401
// which it comes from. The original operation works as a kind of "identifier". Every contextual information
398
402
// gathered during transformation that we want to save for given operation, is actually saved for the original operation.
@@ -583,7 +587,8 @@ class ContextFactory {
583
587
aWasUndone : this . _wasUndone ( opA ) ,
584
588
bWasUndone : this . _wasUndone ( opB ) ,
585
589
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
587
592
} ;
588
593
}
589
594
@@ -1313,7 +1318,7 @@ setTransformation( MergeOperation, MoveOperation, ( a, b, context ) => {
1313
1318
//
1314
1319
const removedRange = Range . _createFromPositionAndShift ( b . sourcePosition , b . howMany ) ;
1315
1320
1316
- if ( b . type == 'remove' && ! context . bWasUndone ) {
1321
+ if ( b . type == 'remove' && ! context . bWasUndone && ! context . forceWeakRemove ) {
1317
1322
if ( a . deletionPosition . hasSameParentAs ( b . sourcePosition ) && removedRange . containsPosition ( a . sourcePosition ) ) {
1318
1323
return [ new NoOperation ( 0 ) ] ;
1319
1324
}
@@ -1596,9 +1601,9 @@ setTransformation( MoveOperation, MoveOperation, ( a, b, context ) => {
1596
1601
//
1597
1602
// If only one of operations is a remove operation, we force remove operation to be the "stronger" one
1598
1603
// 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 ) {
1600
1605
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 ) {
1602
1607
aIsStrong = false ;
1603
1608
}
1604
1609
@@ -1768,7 +1773,7 @@ setTransformation( MoveOperation, SplitOperation, ( a, b, context ) => {
1768
1773
if ( b . graveyardPosition ) {
1769
1774
const movesGraveyardElement = moveRange . start . isEqual ( b . graveyardPosition ) || moveRange . containsPosition ( b . graveyardPosition ) ;
1770
1775
1771
- if ( a . howMany > 1 && movesGraveyardElement ) {
1776
+ if ( a . howMany > 1 && movesGraveyardElement && ! context . aWasUndone ) {
1772
1777
ranges . push ( Range . _createFromPositionAndShift ( b . insertionPosition , 1 ) ) ;
1773
1778
}
1774
1779
}
@@ -1780,7 +1785,7 @@ setTransformation( MoveOperation, MergeOperation, ( a, b, context ) => {
1780
1785
const movedRange = Range . _createFromPositionAndShift ( a . sourcePosition , a . howMany ) ;
1781
1786
1782
1787
if ( b . deletionPosition . hasSameParentAs ( a . sourcePosition ) && movedRange . containsPosition ( b . sourcePosition ) ) {
1783
- if ( a . type == 'remove' ) {
1788
+ if ( a . type == 'remove' && ! context . forceWeakRemove ) {
1784
1789
// Case 1:
1785
1790
//
1786
1791
// The element to remove got merged.
@@ -1794,21 +1799,22 @@ setTransformation( MoveOperation, MergeOperation, ( a, b, context ) => {
1794
1799
const results = [ ] ;
1795
1800
1796
1801
let gyMoveSource = b . graveyardPosition . clone ( ) ;
1797
- let splitNodesMoveSource = b . targetPosition . clone ( ) ;
1802
+ let splitNodesMoveSource = b . targetPosition . _getTransformedByMergeOperation ( b ) ;
1798
1803
1799
1804
if ( a . howMany > 1 ) {
1800
1805
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 ) ;
1802
1808
splitNodesMoveSource = splitNodesMoveSource . _getTransformedByMove ( a . sourcePosition , a . targetPosition , a . howMany - 1 ) ;
1803
1809
}
1804
1810
1805
1811
const gyMoveTarget = b . deletionPosition . _getCombined ( a . sourcePosition , a . targetPosition ) ;
1806
1812
const gyMove = new MoveOperation ( gyMoveSource , 1 , gyMoveTarget , 0 ) ;
1807
1813
1808
- const targetPositionPath = gyMove . getMovedRangeStart ( ) . path . slice ( ) ;
1809
- targetPositionPath . push ( 0 ) ;
1814
+ const splitNodesMoveTargetPath = gyMove . getMovedRangeStart ( ) . path . slice ( ) ;
1815
+ splitNodesMoveTargetPath . push ( 0 ) ;
1810
1816
1811
- const splitNodesMoveTarget = new Position ( gyMove . targetPosition . root , targetPositionPath ) ;
1817
+ const splitNodesMoveTarget = new Position ( gyMove . targetPosition . root , splitNodesMoveTargetPath ) ;
1812
1818
splitNodesMoveSource = splitNodesMoveSource . _getTransformedByMove ( gyMoveSource , gyMoveTarget , 1 ) ;
1813
1819
const splitNodesMove = new MoveOperation ( splitNodesMoveSource , b . howMany , splitNodesMoveTarget , 0 ) ;
1814
1820
@@ -2052,7 +2058,9 @@ setTransformation( SplitOperation, MoveOperation, ( a, b, context ) => {
2052
2058
// is already moved to the correct position, we need to only move the nodes after the split position.
2053
2059
// This will be done by `MoveOperation` instead of `SplitOperation`.
2054
2060
//
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 ) {
2056
2064
const sourcePosition = a . splitPosition . _getTransformedByMoveOperation ( b ) ;
2057
2065
2058
2066
const newParentPosition = a . graveyardPosition . _getTransformedByMoveOperation ( b ) ;
0 commit comments