Skip to content

Commit 7f3f02e

Browse files
authored
Merge pull request #17453 from ckeditor/revert-17427-ck/17267
Revert (engine): Fixed editor crash after pasting over previously pasted content and undoing both actions (#17427).
2 parents e2ba8ff + bc9c3fe commit 7f3f02e

File tree

2 files changed

+8
-106
lines changed

2 files changed

+8
-106
lines changed

packages/ckeditor5-engine/src/model/operation/transform.ts

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -539,17 +539,10 @@ class ContextFactory {
539539
}
540540
} else if ( opA instanceof MergeOperation ) {
541541
if ( opB instanceof MergeOperation ) {
542-
// There can be only relation set for a pair of operations, but it is fine for these cases here.
543-
// There is only one scenario when a pair can fulfill two cases -- `mergeSameTarget` and `mergeSameElement` which happens
544-
// if operations are equal. But in this case it is okay that `mergeSameElement` overwrites `mergeSameTarget`.
545542
if ( !opA.targetPosition.isEqual( opB.sourcePosition ) ) {
546543
this._setRelation( opA, opB, 'mergeTargetNotMoved' );
547544
}
548545

549-
if ( opA.targetPosition.isEqual( opB.targetPosition ) ) {
550-
this._setRelation( opA, opB, 'mergeSameTarget' );
551-
}
552-
553546
if ( opA.sourcePosition.isEqual( opB.targetPosition ) ) {
554547
this._setRelation( opA, opB, 'mergeSourceNotMoved' );
555548
}
@@ -566,10 +559,6 @@ class ContextFactory {
566559
this._setRelation( opA, opB, 'mergeSourceAffected' );
567560
}
568561

569-
// After changes introduced for issue #17267 this branch was not covered anymore by our tests, as the introduced fixes
570-
// covered the test that was earlier covered by this `if`. However, it is not 100% clear, that this cannot happen anymore,
571-
// hence it was decided to keep handling this scenario anyway.
572-
/* istanbul ignore next -- @preserve */
573562
if ( opA.targetPosition.isEqual( opB.sourcePosition ) ) {
574563
this._setRelation( opA, opB, 'mergeTargetWasBefore' );
575564
}
@@ -1392,32 +1381,14 @@ setTransformation( MergeOperation, MergeOperation, ( a, b, context ) => {
13921381
}
13931382

13941383
// The default case.
1384+
// TODO: Possibly, there's a missing case for same `targetPosition` but different `sourcePosition`.
13951385
//
13961386
if ( a.sourcePosition.hasSameParentAs( b.targetPosition ) ) {
13971387
a.howMany += b.howMany;
13981388
}
13991389

14001390
a.sourcePosition = a.sourcePosition._getTransformedByMergeOperation( b );
1401-
1402-
if ( a.targetPosition.isEqual( b.targetPosition ) ) {
1403-
// Case 3:
1404-
//
1405-
// Operations target to the same position (merge something into the same element). But unlike case 1, the operations are not equal,
1406-
// because they have different source position.
1407-
//
1408-
// In most cases, when operations point to the same merge target, the operations are equal (i.e. they also have the same source).
1409-
// However, in some more complex undo cases (including multiple steps) it happens that two different elements are merged into
1410-
// the same element. It only happens "temporarily" as the operation is being transformed during undo process.
1411-
// It doesn't seem that this can happen in real-time collaboration.
1412-
//
1413-
// In this case, simply move the `a` merge target position, so it still points to the end of the target element.
1414-
//
1415-
a.targetPosition = a.targetPosition.getShiftedBy( b.howMany );
1416-
} else {
1417-
// The default case.
1418-
//
1419-
a.targetPosition = a.targetPosition._getTransformedByMergeOperation( b );
1420-
}
1391+
a.targetPosition = a.targetPosition._getTransformedByMergeOperation( b );
14211392

14221393
// Handle positions in graveyard.
14231394
// If graveyard positions are same and `a` operation is strong - do not transform.
@@ -1467,10 +1438,6 @@ setTransformation( MergeOperation, MoveOperation, ( a, b, context ) => {
14671438
// [] is move operation, } is merge source position (sticks to previous by default):
14681439
// <p>A[b]}c</p> -> <p>A}c</p>
14691440
//
1470-
// After changes introduced for issue #17267 two of the else-if branches were not covered anymore by our tests, as the introduced fixes
1471-
// covered the test that was earlier covered by these branches. However, it is not 100% clear, that this cannot happen anymore,
1472-
// hence it was decided to keep handling these scenarios anyway.
1473-
/* istanbul ignore next -- @preserve */
14741441
if ( b.sourcePosition.getShiftedBy( b.howMany ).isEqual( a.sourcePosition ) ) {
14751442
a.sourcePosition.stickiness = 'toNone';
14761443
}
@@ -1642,10 +1609,11 @@ setTransformation( MergeOperation, SplitOperation, ( a, b, context ) => {
16421609
}
16431610

16441611
// This merge operation might have been earlier transformed by a merge operation which both merged the same element.
1645-
// See that case in `MergeOperation` x `MergeOperation` transformation.
1612+
// See that case in `MergeOperation` x `MergeOperation` transformation. In that scenario, if the merge operation has been undone,
1613+
// the special case is not applied.
16461614
//
1647-
// Now, the merge operation is transformed by the split which has undone that previous merge operation. Make sure to set
1648-
// correct properties on merge operation `a`, just like the earlier merge and this split did not happen.
1615+
// Now, the merge operation is transformed by the split which has undone that previous merge operation.
1616+
// So now we are fixing situation which was skipped in `MergeOperation` x `MergeOperation` case.
16491617
//
16501618
if ( context.abRelation == 'mergeSameElement' || a.sourcePosition.offset > 0 ) {
16511619
a.sourcePosition = b.moveTargetPosition.clone();
@@ -1658,31 +1626,11 @@ setTransformation( MergeOperation, SplitOperation, ( a, b, context ) => {
16581626
// The default case.
16591627
//
16601628
if ( a.sourcePosition.hasSameParentAs( b.splitPosition ) ) {
1661-
if ( b.splitPosition.offset >= a.sourcePosition.offset ) {
1662-
a.howMany = b.splitPosition.offset - a.sourcePosition.offset;
1663-
}
1629+
a.howMany = b.splitPosition.offset;
16641630
}
16651631

16661632
a.sourcePosition = a.sourcePosition._getTransformedBySplitOperation( b );
1667-
1668-
if ( a.targetPosition.hasSameParentAs( b.splitPosition ) && context.abRelation == 'mergeSameTarget' ) {
1669-
// Case 3:
1670-
//
1671-
// Merge operation targets into an element which is being split, but unlike case 1, the split happens at different (earlier)
1672-
// position. This is a regular case. By default, the merge target position is moved to the right-hand split part of the split
1673-
// element: <p>Foobar^</p><p>XYZ</p> --> <p>Foo</p><p>bar^</p><p>XYZ</p>. This happens in the default handling below.
1674-
//
1675-
// However, in some cases, we need to do something different. If this split operation is undoing a merge operation, and both
1676-
// `a` merge operation and the undone merge operation were targeting to the same element, we will acknowledge, that this
1677-
// `a` merge operation at the very beginning was supposed to merge into the original element. We will keep the target position in
1678-
// the original element, to ensure proper processing during undo: <p>Foobar^</p><p>XYZ</p> --> <p>Foo^</p><p>bar</p><p>XYZ</p>.
1679-
//
1680-
a.targetPosition = a.targetPosition.getShiftedBy( -b.howMany );
1681-
} else {
1682-
// The default case.
1683-
//
1684-
a.targetPosition = a.targetPosition._getTransformedBySplitOperation( b );
1685-
}
1633+
a.targetPosition = a.targetPosition._getTransformedBySplitOperation( b );
16861634

16871635
return [ a ];
16881636
} );

packages/ckeditor5-engine/tests/model/operation/transform/undo.js

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -930,51 +930,5 @@ describe( 'transform', () => {
930930

931931
expectClients( '<paragraph>ABCD</paragraph>' );
932932
} );
933-
934-
it( 'paste on text, then paste on that paste, then undo, undo, redo, redo', () => {
935-
john.setData( '<paragraph>[]Some text to work with. A sentence to replace. More text to work with</paragraph>' );
936-
937-
// Paste over 'A sentence to replace. '.
938-
john.editor.model.change( writer => {
939-
const root = john.editor.model.document.getRoot();
940-
const range = writer.createRange(
941-
writer.createPositionFromPath( root, [ 0, 24 ] ),
942-
writer.createPositionFromPath( root, [ 0, 47 ] )
943-
);
944-
945-
john.editor.model.insertContent(
946-
new DocumentFragment( [ new Element( 'paragraph', null, new Text( 'First text change. ' ) ) ] ),
947-
range
948-
);
949-
} );
950-
951-
// Paste over 'First text change. '.
952-
john.editor.model.change( writer => {
953-
const root = john.editor.model.document.getRoot();
954-
const range = writer.createRange(
955-
writer.createPositionFromPath( root, [ 0, 24 ] ),
956-
writer.createPositionFromPath( root, [ 0, 43 ] )
957-
);
958-
959-
john.editor.model.insertContent(
960-
new DocumentFragment( [ new Element( 'paragraph', null, new Text( 'Second text change. ' ) ) ] ),
961-
range
962-
);
963-
} );
964-
965-
expectClients( '<paragraph>Some text to work with. Second text change. More text to work with</paragraph>' );
966-
967-
john.undo();
968-
expectClients( '<paragraph>Some text to work with. First text change. More text to work with</paragraph>' );
969-
970-
john.undo();
971-
expectClients( '<paragraph>Some text to work with. A sentence to replace. More text to work with</paragraph>' );
972-
973-
john.redo();
974-
expectClients( '<paragraph>Some text to work with. First text change. More text to work with</paragraph>' );
975-
976-
john.redo();
977-
expectClients( '<paragraph>Some text to work with. Second text change. More text to work with</paragraph>' );
978-
} );
979933
} );
980934
} );

0 commit comments

Comments
 (0)