ソースを参照

Merge pull request #1558 from ckeditor/t/1557

Internal: Added merge x merge transformation case handling when merges source position is the same but the target is different. Closes #1557.
Piotr Jasiun 7 年 前
コミット
ebf58622e1

+ 50 - 0
packages/ckeditor5-engine/src/model/operation/transform.js

@@ -1247,6 +1247,56 @@ setTransformation( MergeOperation, MergeOperation, ( a, b, context ) => {
 		}
 	}
 
+	// Case 2:
+	//
+	// Same merge source position but different target position.
+	//
+	// This can happen during collaboration. For example, if one client merged a paragraph to the previous paragraph
+	// and the other person removed that paragraph and merged the same paragraph to something before:
+	//
+	// Client A:
+	// <p>Foo</p><p>Bar</p><p>[]Xyz</p>
+	// <p>Foo</p><p>BarXyz</p>
+	//
+	// Client B:
+	// <p>Foo</p>[<p>Bar</p>]<p>Xyz</p>
+	// <p>Foo</p><p>[]Xyz</p>
+	// <p>FooXyz</p>
+	//
+	// In this case we need to decide where finally "Xyz" will land:
+	//
+	// <p>FooXyz</p>               graveyard: <p>Bar</p>
+	// <p>Foo</p>                  graveyard: <p>BarXyz</p>
+	//
+	// Let's move it in a way so that a merge operation that does not target to graveyard is more important so that
+	// nodes does not end up in the graveyard. It makes sense. Both for Client A and for Client B "Xyz" finally did not
+	// end up in the graveyard (see above).
+	//
+	// If neither or both operations point to graveyard, then let `aIsStrong` decide.
+	//
+	if ( a.sourcePosition.isEqual( b.sourcePosition ) && !a.targetPosition.isEqual( b.targetPosition ) && !context.bWasUndone ) {
+		const aToGraveyard = a.targetPosition.root.rootName == '$graveyard';
+		const bToGraveyard = b.targetPosition.root.rootName == '$graveyard';
+
+		// If `aIsWeak` it means that `a` points to graveyard while `b` doesn't. Don't move nodes then.
+		const aIsWeak = aToGraveyard && !bToGraveyard;
+
+		// If `bIsWeak` it means that `b` points to graveyard while `a` doesn't. Force moving nodes then.
+		const bIsWeak = bToGraveyard && !aToGraveyard;
+
+		// Force move if `b` is weak or neither operation is weak but `a` is stronger through `context.aIsStrong`.
+		const forceMove = bIsWeak || ( !aIsWeak && context.aIsStrong );
+
+		if ( forceMove ) {
+			const sourcePosition = b.targetPosition._getTransformedByMergeOperation( b );
+			const targetPosition = a.targetPosition._getTransformedByMergeOperation( b );
+
+			return [ new MoveOperation( sourcePosition, a.howMany, targetPosition, 0 ) ];
+		} else {
+			return [ new NoOperation( 0 ) ];
+		}
+	}
+
 	// The default case.
 	//
 	if ( a.sourcePosition.hasSameParentAs( b.targetPosition ) ) {

+ 12 - 0
packages/ckeditor5-engine/tests/model/operation/transform/merge.js

@@ -116,6 +116,18 @@ describe( 'transform', () => {
 
 				expectClients( '<paragraph>For</paragraph>' );
 			} );
+
+			it( 'merged elements and some text', () => {
+				john.setData( '<paragraph>Foo</paragraph><paragraph></paragraph>[]<paragraph>Bar</paragraph>' );
+				kate.setData( '<paragraph>F[oo</paragraph><paragraph></paragraph><paragraph>Ba]r</paragraph>' );
+
+				john.merge();
+				kate.delete();
+
+				syncClients();
+
+				expectClients( '<paragraph>Fr</paragraph>' );
+			} );
 		} );
 
 		describe( 'by wrap', () => {