Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 033e850

Browse files
authored
Merge pull request #1052 from ckeditor/t/1051
Fixed: Fixed a bug when renaming followed by merge or split resulted in multiple elements being incorrectly renamed during undo. Closes #1051.
2 parents 331d2f4 + 078a4e8 commit 033e850

File tree

3 files changed

+56
-27
lines changed

3 files changed

+56
-27
lines changed

src/model/delta/basic-transformations.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -348,15 +348,16 @@ addTransformationCase( WrapDelta, SplitDelta, ( a, b, context ) => {
348348

349349
// Add special case for RenameDelta x SplitDelta transformation.
350350
addTransformationCase( RenameDelta, SplitDelta, ( a, b, context ) => {
351-
const splitPosition = new Position( b.position.root, b.position.path.slice( 0, -1 ) );
351+
const undoMode = context.aWasUndone || context.bWasUndone;
352+
const posBeforeSplitParent = new Position( b.position.root, b.position.path.slice( 0, -1 ) );
352353

353354
const deltas = defaultTransform( a, b, context );
354355

355-
if ( a.operations[ 0 ].position.isEqual( splitPosition ) ) {
356+
if ( !undoMode && a.operations[ 0 ].position.isEqual( posBeforeSplitParent ) ) {
356357
// If a node that has been split has it's name changed, we should also change name of
357358
// the node created during splitting.
358359
const additionalRenameDelta = a.clone();
359-
additionalRenameDelta.operations[ 0 ].position = a.operations[ 0 ].position.getShiftedBy( 1 );
360+
additionalRenameDelta.operations[ 0 ].position = posBeforeSplitParent.getShiftedBy( 1 );
360361

361362
deltas.push( additionalRenameDelta );
362363
}
@@ -365,20 +366,25 @@ addTransformationCase( RenameDelta, SplitDelta, ( a, b, context ) => {
365366
} );
366367

367368
// Add special case for SplitDelta x RenameDelta transformation.
368-
addTransformationCase( SplitDelta, RenameDelta, ( a, b ) => {
369-
a = a.clone();
370-
371-
const splitPosition = new Position( a.position.root, a.position.path.slice( 0, -1 ) );
372-
373-
if ( a._cloneOperation instanceof InsertOperation ) {
374-
// If element to split had it's name changed, we have to reflect this change in an element
375-
// that is in SplitDelta's InsertOperation.
376-
if ( b.operations[ 0 ].position.isEqual( splitPosition ) ) {
377-
a._cloneOperation.nodes.getNode( 0 ).name = b.operations[ 0 ].newName;
378-
}
369+
addTransformationCase( SplitDelta, RenameDelta, ( a, b, context ) => {
370+
const undoMode = context.aWasUndone || context.bWasUndone;
371+
const posBeforeSplitParent = new Position( a.position.root, a.position.path.slice( 0, -1 ) );
372+
373+
// If element to split had it's name changed, we have to reflect this by creating additional rename operation.
374+
if ( !undoMode && b.operations[ 0 ].position.isEqual( posBeforeSplitParent ) ) {
375+
const additionalRenameDelta = b.clone();
376+
additionalRenameDelta.operations[ 0 ].position = posBeforeSplitParent.getShiftedBy( 1 );
377+
378+
// `nodes` is a property that is available only if `SplitDelta` `a` has `InsertOperation`.
379+
// `SplitDelta` may have `ReinsertOperation` instead of `InsertOperation`.
380+
// However, such delta is only created when `MergeDelta` is reversed.
381+
// So if this is not undo mode, it means that `SplitDelta` has `InsertOperation`.
382+
additionalRenameDelta.operations[ 0 ].oldName = a._cloneOperation.nodes.getNode( 0 ).name;
383+
384+
return [ a.clone(), additionalRenameDelta ];
379385
}
380386

381-
return [ a ];
387+
return [ a.clone() ];
382388
} );
383389

384390
// Add special case for RemoveDelta x SplitDelta transformation.

src/model/delta/transform.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,18 @@ const transform = {
262262
insertBefore: contextAB.insertBefore,
263263
forceNotSticky: contextAB.forceNotSticky,
264264
isStrong: contextAB.isStrong,
265-
forceWeakRemove: contextAB.forceWeakRemove
265+
forceWeakRemove: contextAB.forceWeakRemove,
266+
aWasUndone: false,
267+
bWasUndone: contextAB.bWasUndone
266268
} );
267269

268270
const resultBA = transform.transform( deltaB[ l ], deltaA[ k ], {
269271
insertBefore: !contextAB.insertBefore,
270272
forceNotSticky: contextAB.forceNotSticky,
271273
isStrong: !contextAB.isStrong,
272-
forceWeakRemove: contextAB.forceWeakRemove
274+
forceWeakRemove: contextAB.forceWeakRemove,
275+
aWasUndone: contextAB.bWasUndone,
276+
bWasUndone: false
273277
} );
274278

275279
if ( useAdditionalContext ) {
@@ -350,10 +354,21 @@ function padWithNoOps( deltas, howMany ) {
350354
// Using data given in `context` object, sets `context.insertBefore` and `context.forceNotSticky` flags.
351355
// Also updates `context.wasAffected`.
352356
function _setContext( a, b, context ) {
357+
_setBWasUndone( b, context );
353358
_setWasAffected( a, b, context );
354359
_setInsertBeforeContext( a, b, context );
355360
_setForceWeakRemove( b, context );
356-
_setForceNotSticky( b, context );
361+
_setForceNotSticky( context );
362+
}
363+
364+
// Sets `context.bWasUndone` basing on `context.document` history for `b` delta.
365+
//
366+
// `context.bWasUndone` is set to `true` if the (originally transformed) `b` delta was undone or was undoing delta.
367+
function _setBWasUndone( b, context ) {
368+
const originalDelta = context.originalDelta.get( b );
369+
const history = context.document.history;
370+
371+
context.bWasUndone = history.isUndoneDelta( originalDelta ) || history.isUndoingDelta( originalDelta );
357372
}
358373

359374
// Sets `context.insertBefore` basing on `context.document` history for `a` by `b` transformation.
@@ -417,12 +432,8 @@ function _setInsertBeforeContext( a, b, context ) {
417432
// if delta `b` was already undone or if delta `b` is an undoing delta.
418433
//
419434
// This affects `MoveOperation` (and its derivatives).
420-
function _setForceNotSticky( b, context ) {
421-
const history = context.document.history;
422-
const originalB = context.originalDelta.get( b );
423-
424-
// If `b` delta is undoing or undone delta, stickiness should not be taken into consideration.
425-
if ( history.isUndoingDelta( originalB ) || history.isUndoneDelta( originalB ) ) {
435+
function _setForceNotSticky( context ) {
436+
if ( context.bWasUndone ) {
426437
context.forceNotSticky = true;
427438
}
428439
}

tests/model/delta/transform/splitdelta.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -625,14 +625,14 @@ describe( 'transform', () => {
625625
it( 'renamed split element', () => {
626626
const renameDelta = new RenameDelta();
627627
renameDelta.addOperation( new RenameOperation(
628-
new Position( root, [ 3, 3, 3 ] ), 'p', 'li', baseVersion
628+
new Position( root, [ 3, 3, 3 ] ), 'h2', 'li', baseVersion
629629
) );
630630

631631
const transformed = transform( splitDelta, renameDelta, context );
632632

633633
baseVersion = renameDelta.operations.length;
634634

635-
expect( transformed.length ).to.equal( 1 );
635+
expect( transformed.length ).to.equal( 2 );
636636

637637
expectDelta( transformed[ 0 ], {
638638
type: SplitDelta,
@@ -652,7 +652,18 @@ describe( 'transform', () => {
652652
]
653653
} );
654654

655-
expect( transformed[ 0 ].operations[ 0 ].nodes.getNode( 0 ).name ).to.equal( 'li' );
655+
expectDelta( transformed[ 1 ], {
656+
type: RenameDelta,
657+
operations: [
658+
{
659+
type: RenameOperation,
660+
position: new Position( root, [ 3, 3, 4 ] ),
661+
oldName: 'p', // `oldName` taken from SplitDelta.
662+
newName: 'li',
663+
baseVersion: baseVersion + 2
664+
}
665+
]
666+
} );
656667
} );
657668

658669
it( 'split element is different than renamed element', () => {
@@ -699,6 +710,7 @@ describe( 'transform', () => {
699710
new Position( root, [ 3, 3, 3 ] ), 'p', 'li', baseVersion
700711
) );
701712

713+
context.aWasUndone = true;
702714
const transformed = transform( splitDelta, renameDelta, context );
703715

704716
baseVersion = renameDelta.operations.length;

0 commit comments

Comments
 (0)