Procházet zdrojové kódy

Merge branch 'master' of github.com:ckeditor/ckeditor5-engine into t/788

Oskar Wróbel před 9 roky
rodič
revize
94f395c668
30 změnil soubory, kde provedl 197 přidání a 121 odebrání
  1. 1 1
      packages/ckeditor5-engine/src/controller/deletecontent.js
  2. 7 0
      packages/ckeditor5-engine/src/model/delta/attributedelta.js
  3. 8 18
      packages/ckeditor5-engine/src/model/delta/basic-transformations.js
  4. 7 0
      packages/ckeditor5-engine/src/model/delta/delta.js
  5. 7 0
      packages/ckeditor5-engine/src/model/delta/insertdelta.js
  6. 7 0
      packages/ckeditor5-engine/src/model/delta/markerdelta.js
  7. 7 0
      packages/ckeditor5-engine/src/model/delta/mergedelta.js
  8. 7 0
      packages/ckeditor5-engine/src/model/delta/movedelta.js
  9. 7 0
      packages/ckeditor5-engine/src/model/delta/renamedelta.js
  10. 7 0
      packages/ckeditor5-engine/src/model/delta/splitdelta.js
  11. 7 0
      packages/ckeditor5-engine/src/model/delta/unwrapdelta.js
  12. 7 0
      packages/ckeditor5-engine/src/model/delta/wrapdelta.js
  13. 1 3
      packages/ckeditor5-engine/src/model/document.js
  14. 10 16
      packages/ckeditor5-engine/src/model/liverange.js
  15. 1 0
      packages/ckeditor5-engine/src/model/operation/operation.js
  16. 2 0
      packages/ckeditor5-engine/src/model/position.js
  17. 21 26
      packages/ckeditor5-engine/src/model/range.js
  18. 6 0
      packages/ckeditor5-engine/tests/model/delta/attributedelta.js
  19. 6 0
      packages/ckeditor5-engine/tests/model/delta/insertdelta.js
  20. 6 0
      packages/ckeditor5-engine/tests/model/delta/markerdelta.js
  21. 6 0
      packages/ckeditor5-engine/tests/model/delta/mergedelta.js
  22. 6 0
      packages/ckeditor5-engine/tests/model/delta/movedelta.js
  23. 6 0
      packages/ckeditor5-engine/tests/model/delta/renamedelta.js
  24. 6 0
      packages/ckeditor5-engine/tests/model/delta/splitdelta.js
  25. 6 0
      packages/ckeditor5-engine/tests/model/delta/unwrapdelta.js
  26. 6 0
      packages/ckeditor5-engine/tests/model/delta/wrapdelta.js
  27. 3 1
      packages/ckeditor5-engine/tests/model/document/document.js
  28. 4 21
      packages/ckeditor5-engine/tests/model/liverange.js
  29. 20 15
      packages/ckeditor5-engine/tests/model/liveselection.js
  30. 2 20
      packages/ckeditor5-engine/tests/model/range.js

+ 1 - 1
packages/ckeditor5-engine/src/controller/deletecontent.js

@@ -33,7 +33,7 @@ export default function deleteContent( selection, batch, options = {} ) {
 	const endPos = LivePosition.createFromPosition( selRange.end );
 
 	// 1. Remove the contents if there are any.
-	if ( !selRange.isEmpty ) {
+	if ( !selRange.start.isTouching( selRange.end ) ) {
 		batch.remove( selRange );
 	}
 

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/attributedelta.js

@@ -24,6 +24,13 @@ import Element from '../element';
  * @extends module:engine/model/delta/delta~Delta
  */
 export default class AttributeDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'attribute';
+	}
+
 	/**
 	 * The attribute key that is changed by the delta or `null` if the delta has no operations.
 	 *

+ 8 - 18
packages/ckeditor5-engine/src/model/delta/basic-transformations.js

@@ -103,33 +103,23 @@ addTransformationCase( MarkerDelta, SplitDelta, ( a, b, isStrong ) => {
 	// ab[cdef]gh   ==>  ab[cd
 	//                   ef]gh
 	// To mimic what normally happens with LiveRange if you split it.
-	const markerOp = a.operations[ 0 ];
 
-	let oldRangeEndPosition = null;
-	let newRangeEndPosition = null;
+	// Mote: MarkerDelta can't get split to two deltas, neither can MarkerOperation.
+	const transformedDelta = defaultTransform( a, b, isStrong )[ 0 ];
+	const transformedOp = transformedDelta.operations[ 0 ];
+
+	// Fix positions, if needed.
+	const markerOp = a.operations[ 0 ];
 
 	const source = b.position;
 	const target = b._moveOperation.targetPosition;
 
 	if ( markerOp.oldRange.containsPosition( b.position ) ) {
-		oldRangeEndPosition = markerOp.oldRange.end._getCombined( source, target );
+		transformedOp.oldRange.end = markerOp.oldRange.end._getCombined( source, target );
 	}
 
 	if ( markerOp.newRange.containsPosition( b.position ) ) {
-		newRangeEndPosition = markerOp.newRange.end._getCombined( source, target );
-	}
-
-	// MarkerDelta can't get split to two deltas, neither can MarkerOperation.
-	const transformedDelta = defaultTransform( a, b, isStrong )[ 0 ];
-	const transformedOp = transformedDelta.operations[ 0 ];
-
-	// Fix positions.
-	if ( oldRangeEndPosition ) {
-		transformedOp.oldRange.end = oldRangeEndPosition;
-	}
-
-	if ( newRangeEndPosition ) {
-		transformedOp.newRange.end = newRangeEndPosition;
+		transformedOp.newRange.end = markerOp.newRange.end._getCombined( source, target );
 	}
 
 	return [ transformedDelta ];

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/delta.js

@@ -75,6 +75,13 @@ export default class Delta {
 		return Delta;
 	}
 
+	/**
+	 * Delta type.
+	 *
+	 * @readonly
+	 * @member {String} #type
+	 */
+
 	/**
 	 * Add operation to the delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/insertdelta.js

@@ -19,6 +19,13 @@ import InsertOperation from '../operation/insertoperation';
  * uses the `InsertDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class InsertDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'insert';
+	}
+
 	/**
 	 * Position where the delta inserts nodes or `null` if there are no operations in the delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/markerdelta.js

@@ -20,6 +20,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * from the `Delta` class and may overwrite some methods.
  */
 export default class MarkerDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'marker';
+	}
+
 	/**
 	 * A class that will be used when creating reversed delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/mergedelta.js

@@ -23,6 +23,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * uses the `MergeDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class MergeDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'merge';
+	}
+
 	/**
 	 * Position between to merged nodes or `null` if the delta has no operations.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/movedelta.js

@@ -21,6 +21,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * uses the `MoveDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class MoveDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'move';
+	}
+
 	/**
 	 * Offset size of moved range or `null` if there are no operations in the delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/renamedelta.js

@@ -20,6 +20,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * uses the `RenameDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class RenameDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'rename';
+	}
+
 	/**
 	 * @inheritDoc
 	 */

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/splitdelta.js

@@ -23,6 +23,13 @@ import MergeDelta from '../delta/mergedelta';
  * uses `SplitDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class SplitDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'split';
+	}
+
 	/**
 	 * Position of split or `null` if there are no operations in the delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/unwrapdelta.js

@@ -22,6 +22,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * uses the `UnwrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class UnwrapDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'unwrap';
+	}
+
 	/**
 	 * Position before unwrapped element or `null` if there are no operations in the delta.
 	 *

+ 7 - 0
packages/ckeditor5-engine/src/model/delta/wrapdelta.js

@@ -24,6 +24,13 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  * uses the `WrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
  */
 export default class WrapDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
+	get type() {
+		return 'wrap';
+	}
+
 	/**
 	 * Range to wrap or `null` if there are no operations in the delta.
 	 *

+ 1 - 3
packages/ckeditor5-engine/src/model/document.js

@@ -166,11 +166,9 @@ export default class Document {
 
 		this.history.addDelta( operation.delta );
 
-		const batch = operation.delta && operation.delta.batch;
-
 		if ( changes ) {
 			// `NoOperation` returns no changes, do not fire event for it.
-			this.fire( 'change', operation.type, changes, batch );
+			this.fire( 'change', operation.type, changes, operation.delta.batch, operation.delta.type );
 		}
 	}
 

+ 10 - 16
packages/ckeditor5-engine/src/model/liverange.js

@@ -101,9 +101,9 @@ function bindWithDocument() {
 	this.listenTo(
 		this.root.document,
 		'change',
-		( event, type, changes ) => {
+		( event, type, changes, batch, deltaType ) => {
 			if ( supportedTypes.has( type ) ) {
-				transform.call( this, type, changes.range, changes.sourcePosition );
+				transform.call( this, type, deltaType, changes.range, changes.sourcePosition );
 			}
 		},
 		{ priority: 'high' }
@@ -116,36 +116,30 @@ function bindWithDocument() {
  * @ignore
  * @private
  * @method transform
- * @param {String} type Type of changes applied to the model document.
+ * @param {String} [changeType] Type of change applied to the model document.
+ * @param {String} [deltaType] Type of delta which introduced the change.
  * @param {module:engine/model/range~Range} targetRange Range containing the result of applied change.
  * @param {module:engine/model/position~Position} [sourcePosition] Source position for move, remove and reinsert change types.
  */
-function transform( type, targetRange, sourcePosition ) {
+function transform( changeType, deltaType, targetRange, sourcePosition ) {
 	/* jshint validthis: true */
 	const howMany = targetRange.end.offset - targetRange.start.offset;
 	let targetPosition = targetRange.start;
 
-	if ( type == 'move' ) {
+	if ( changeType == 'move' ) {
 		// Range._getTransformedByDocumentChange is expecting `targetPosition` to be "before" move
 		// (before transformation). `targetRange.start` is already after the move happened.
 		// We have to revert `targetPosition` to the state before the move.
 		targetPosition = targetPosition._getTransformedByInsertion( sourcePosition, howMany );
 	}
 
-	const result = this._getTransformedByDocumentChange( type, targetPosition, howMany, sourcePosition );
+	const result = this._getTransformedByDocumentChange( changeType, deltaType, targetPosition, howMany, sourcePosition );
 
-	// Decide whether inserted part should be included in the range.
-	// This extra step is needed only for `move` change type and only if the ranges have common part.
-	// If ranges are not intersecting and `targetRange` is moved to this range, it is included (in `_getTransformedByDocumentChange`).
+	// Decide whether moved part should be included in the range.
 	//
-	// If ranges are intersecting, `result` contains spread range. `targetRange` need to be included if it is moved
-	// to this range, but only if it is moved to a position that is not touching this range boundary.
-	//
-	// First, this concerns only `move` change, because insert change includes inserted part always (type == 'move').
+	// First, this concerns only `move` change, because insert change includes inserted part always (changeType == 'move').
 	// Second, this is a case only if moved range was intersecting with this range and was inserted into this range (result.length == 3).
-	// Third, we check range `result[ 1 ]`, that is the range created by splitting this range by inserting `targetRange`.
-	// If that range is not empty, it means that we are in fact inserting inside `targetRange`.
-	if ( type == 'move' && result.length == 3 && !result[ 1 ].isEmpty ) {
+	if ( changeType == 'move' && result.length == 3 ) {
 		// `result[ 2 ]` is a "common part" of this range and moved range. We substitute that common part with the whole
 		// `targetRange` because we want to include whole `targetRange` in this range.
 		result[ 2 ] = targetRange;

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

@@ -33,6 +33,7 @@ export default class Operation {
 		/**
 		 * Operation type.
 		 *
+		 * @readonly
 		 * @member {String} #type
 		 */
 

+ 2 - 0
packages/ckeditor5-engine/src/model/position.js

@@ -363,6 +363,8 @@ export default class Position {
 	 * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
 	 * they are very similar or even indistinguishable.
 	 *
+	 * **Note:** this method traverses model document so it can be only used when range is up-to-date with model document.
+	 *
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {Boolean} True if positions touch.
 	 */

+ 21 - 26
packages/ckeditor5-engine/src/model/range.js

@@ -77,18 +77,6 @@ export default class Range {
 		return this.start.parent === this.end.parent;
 	}
 
-	/**
-	 * Returns whether this range has no nodes in it, that is if {@link #start start} position and
-	 * {@link #end end} position are {@link module:engine/model/position~Position#isTouching touching}.
-	 *
-	 * **Note:** A range may be empty, but not {@link #isCollapsed collapsed}.
-	 *
-	 * @type {Boolean}
-	 */
-	get isEmpty() {
-		return this.start.isTouching( this.end );
-	}
-
 	/**
 	 * Range root element.
 	 *
@@ -390,6 +378,7 @@ export default class Range {
 				for ( let i = 0; i < ranges.length; i++ ) {
 					const result = ranges[ i ]._getTransformedByDocumentChange(
 						operation.type,
+						delta.type,
 						operation.targetPosition || operation.position,
 						operation.howMany || operation.nodes.maxOffset,
 						operation.sourcePosition
@@ -451,16 +440,24 @@ export default class Range {
 	 *
 	 * @protected
 	 * @param {'insert'|'move'|'remove'|'reinsert'} type Change type.
+	 * @param {String} deltaType Type of delta that introduced the change.
 	 * @param {module:engine/model/position~Position} targetPosition Position before the first changed node.
 	 * @param {Number} howMany How many nodes has been changed.
 	 * @param {module:engine/model/position~Position} sourcePosition Source position of changes.
 	 * @returns {Array.<module:engine/model/range~Range>}
 	 */
-	_getTransformedByDocumentChange( type, targetPosition, howMany, sourcePosition ) {
+	_getTransformedByDocumentChange( type, deltaType, targetPosition, howMany, sourcePosition ) {
 		if ( type == 'insert' ) {
 			return this._getTransformedByInsertion( targetPosition, howMany, false, false );
 		} else {
-			return this._getTransformedByMove( sourcePosition, targetPosition, howMany );
+			const ranges = this._getTransformedByMove( sourcePosition, targetPosition, howMany );
+
+			if ( deltaType == 'split' && this.containsPosition( sourcePosition ) ) {
+				ranges[ 0 ].end = ranges[ 1 ].end;
+				ranges.pop();
+			}
+
+			return ranges;
 		}
 	}
 
@@ -533,8 +530,6 @@ export default class Range {
 	 * @param {module:engine/model/position~Position} sourcePosition Position from which nodes are moved.
 	 * @param {module:engine/model/position~Position} targetPosition Position to where nodes are moved.
 	 * @param {Number} howMany How many nodes are moved.
-	 * @param {Boolean} [spread] Flag indicating whether this {~Range range} should be spread if insertion
-	 * was inside the range. Defaults to `false`.
 	 * @returns {Array.<module:engine/model/range~Range>} Result of the transformation.
 	 */
 	_getTransformedByMove( sourcePosition, targetPosition, howMany ) {
@@ -651,8 +646,8 @@ export default class Range {
 	 * Combines all ranges from the passed array into a one range. At least one range has to be passed.
 	 * Passed ranges must not have common parts.
 	 *
-	 * The first range from the array is a reference range. If other ranges
-	 * {@link module:engine/model/position~Position#isTouching are touching} the reference range, they will get combined into one range.
+	 * The first range from the array is a reference range. If other ranges starts or ends on the exactly same position where
+	 * the reference range, they get combined into one range.
 	 *
 	 *		[  ][]  [    ][ ][  ref range  ][ ][]  [  ]  // Passed ranges, shown sorted. "Ref range" was the first range in original array.
 	 *		        [      returned range       ]  [  ]  // The combined range.
@@ -691,24 +686,24 @@ export default class Range {
 		// We have to create a copy of the reference range.
 		const result = new this( ref.start, ref.end );
 
-		// 5. Ranges before reference range should be glued starting from the "last one", that is the range
-		// that is closest to the reference range.
+		// 5. Ranges should be checked and glued starting from the range that is closest to the reference range.
+		// Since ranges are sorted, start with the range with index that is closest to reference range index.
 		for ( let i = refIndex - 1; i >= 0; i++ ) {
-			if ( ranges[ i ].end.isTouching( result.start ) ) {
+			if ( ranges[ i ].end.isEqual( result.start ) ) {
 				result.start = Position.createFromPosition( ranges[ i ].start );
 			} else {
-				// If range do not touch with reference range there is no point in looking further.
+				// If ranges are not starting/ending at the same position there is no point in looking further.
 				break;
 			}
 		}
 
-		// 5. Ranges after reference range should be glued starting from the "first one", that is the range
-		// that is closest to the reference range.
+		// 6. Ranges should be checked and glued starting from the range that is closest to the reference range.
+		// Since ranges are sorted, start with the range with index that is closest to reference range index.
 		for ( let i = refIndex + 1; i < ranges.length; i++ ) {
-			if ( ranges[ i ].start.isTouching( result.end ) ) {
+			if ( ranges[ i ].start.isEqual( result.end ) ) {
 				result.end = Position.createFromPosition( ranges[ i ].end );
 			} else {
-				// If range do not touch with reference range there is no point in looking further.
+				// If ranges are not starting/ending at the same position there is no point in looking further.
 				break;
 			}
 		}

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/attributedelta.js

@@ -404,6 +404,12 @@ describe( 'AttributeDelta', () => {
 		delta = new AttributeDelta();
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to attribute', () => {
+			expect( delta.type ).to.equal( 'attribute' );
+		} );
+	} );
+
 	describe( 'key', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( delta.key ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/insertdelta.js

@@ -70,6 +70,12 @@ describe( 'InsertDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to insert', () => {
+			expect( insertDelta.type ).to.equal( 'insert' );
+		} );
+	} );
+
 	describe( 'position', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( insertDelta.position ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/markerdelta.js

@@ -136,6 +136,12 @@ describe( 'MarkerDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to marker', () => {
+			expect( markerDelta.type ).to.equal( 'marker' );
+		} );
+	} );
+
 	describe( 'getReversed', () => {
 		it( 'should return correct MarkerDelta', () => {
 			markerDelta.addOperation( new MarkerOperation( 'name', null, range, 0 ) );

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/mergedelta.js

@@ -90,6 +90,12 @@ describe( 'MergeDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to merge', () => {
+			expect( mergeDelta.type ).to.equal( 'merge' );
+		} );
+	} );
+
 	describe( 'position', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( mergeDelta.position ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/movedelta.js

@@ -91,6 +91,12 @@ describe( 'MoveDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to move', () => {
+			expect( moveDelta.type ).to.equal( 'move' );
+		} );
+	} );
+
 	describe( 'sourcePosition', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( moveDelta.sourcePosition ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/renamedelta.js

@@ -70,6 +70,12 @@ describe( 'RenameDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to rename', () => {
+			expect( renameDelta.type ).to.equal( 'rename' );
+		} );
+	} );
+
 	describe( 'getReversed', () => {
 		it( 'should return instance of RenameDelta', () => {
 			let reversed = renameDelta.getReversed();

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/splitdelta.js

@@ -107,6 +107,12 @@ describe( 'SplitDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to split', () => {
+			expect( splitDelta.type ).to.equal( 'split' );
+		} );
+	} );
+
 	describe( 'position', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( splitDelta.position ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/unwrapdelta.js

@@ -78,6 +78,12 @@ describe( 'UnwrapDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to unwrap', () => {
+			expect( unwrapDelta.type ).to.equal( 'unwrap' );
+		} );
+	} );
+
 	describe( 'position', () => {
 		it( 'should be null if there are no operations in delta', () => {
 			expect( unwrapDelta.position ).to.be.null;

+ 6 - 0
packages/ckeditor5-engine/tests/model/delta/wrapdelta.js

@@ -112,6 +112,12 @@ describe( 'WrapDelta', () => {
 		} );
 	} );
 
+	describe( 'type', () => {
+		it( 'should be equal to wrap', () => {
+			expect( wrapDelta.type ).to.equal( 'wrap' );
+		} );
+	} );
+
 	describe( 'range', () => {
 		it( 'should be equal to null if there are no operations in delta', () => {
 			expect( wrapDelta.range ).to.be.null;

+ 3 - 1
packages/ckeditor5-engine/tests/model/document/document.js

@@ -114,6 +114,7 @@ describe( 'Document', () => {
 			const data = { data: 'x' };
 			const batch = new Batch();
 			const delta = new Delta();
+			delta.type = 'type';
 
 			let operation = {
 				type: type,
@@ -133,7 +134,8 @@ describe( 'Document', () => {
 			sinon.assert.calledOnce( changeCallback );
 			expect( changeCallback.args[ 0 ][ 1 ] ).to.equal( type );
 			expect( changeCallback.args[ 0 ][ 2 ] ).to.equal( data );
-			expect( changeCallback.args[ 0 ][ 3 ] ).to.equal( batch );
+			expect( changeCallback.args[ 0 ][ 3 ] ).to.deep.equal( batch );
+			expect( changeCallback.args[ 0 ][ 4 ] ).to.equal( delta.type );
 		} );
 
 		it( 'should throw an error on the operation base version and the document version is different', () => {

+ 4 - 21
packages/ckeditor5-engine/tests/model/liverange.js

@@ -295,26 +295,9 @@ describe( 'LiveRange', () => {
 				expect( spy.calledOnce ).to.be.true;
 			} );
 
-			it( 'intersects on live range left side and live range new start is touching moved range end', () => {
-				let moveSource = new Position( root, [ 0, 1, 0 ] );
-				let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
-
-				let changes = {
-					range: moveRange,
-					sourcePosition: moveSource
-				};
-				doc.fire( 'change', 'move', changes, null );
-
-				expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
-				expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
-				expect( spy.calledOnce ).to.be.true;
-			} );
-
-			it( 'intersects on live range right side and live range new end is touching moved range start', () => {
-				live.end.offset = 12;
-
-				let moveSource = new Position( root, [ 0, 2, 10 ] );
-				let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
+			it( 'intersects with live range and is moved into live range', () => {
+				let moveSource = new Position( root, [ 0, 2, 1 ] );
+				let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 5 ] ) );
 
 				let changes = {
 					range: moveRange,
@@ -323,7 +306,7 @@ describe( 'LiveRange', () => {
 				doc.fire( 'change', 'move', changes, null );
 
 				expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
-				expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
+				expect( live.end.path ).to.deep.equal( [ 0, 2, 6 ] );
 				expect( spy.calledOnce ).to.be.true;
 			} );
 

+ 20 - 15
packages/ckeditor5-engine/tests/model/liveselection.js

@@ -14,6 +14,7 @@ import InsertOperation from '../../src/model/operation/insertoperation';
 import MoveOperation from '../../src/model/operation/moveoperation';
 import RemoveOperation from '../../src/model/operation/removeoperation';
 import AttributeOperation from '../../src/model/operation/attributeoperation';
+import SplitDelta from '../../src/model/delta/splitdelta';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
@@ -461,22 +462,26 @@ describe( 'LiveSelection', () => {
 					expect( data.directChange ).to.be.false;
 				} );
 
-				doc.applyOperation( wrapInDelta(
-					new InsertOperation(
-						new Position( root, [ 2 ] ),
-						new Element( 'p' ),
-						doc.version
-					)
-				) );
+				const splitDelta = new SplitDelta();
 
-				doc.applyOperation( wrapInDelta(
-					new MoveOperation(
-						new Position( root, [ 1, 2 ] ),
-						4,
-						new Position( root, [ 2, 0 ] ),
-						doc.version
-					)
-				) );
+				const insertOperation = new InsertOperation(
+					new Position( root, [ 2 ] ),
+					new Element( 'p' ),
+					0
+				);
+
+				const moveOperation = new MoveOperation(
+					new Position( root, [ 1, 2 ] ),
+					4,
+					new Position( root, [ 2, 0 ] ),
+					1
+				);
+
+				splitDelta.addOperation( insertOperation );
+				splitDelta.addOperation( moveOperation );
+
+				doc.applyOperation( insertOperation );
+				doc.applyOperation( moveOperation );
 
 				let range = selection.getFirstRange();
 

+ 2 - 20
packages/ckeditor5-engine/tests/model/range.js

@@ -832,11 +832,9 @@ describe( 'Range', () => {
 
 				const transformed = range.getTransformedByDelta( delta );
 
-				expect( transformed.length ).to.equal( 2 );
+				expect( transformed.length ).to.equal( 1 );
 				expect( transformed[ 0 ].start.path ).to.deep.equal( [ 0, 2 ] );
-				expect( transformed[ 0 ].end.path ).to.deep.equal( [ 0, 3 ] );
-				expect( transformed[ 1 ].start.path ).to.deep.equal( [ 1, 0 ] );
-				expect( transformed[ 1 ].end.path ).to.deep.equal( [ 1, 1 ] );
+				expect( transformed[ 0 ].end.path ).to.deep.equal( [ 1, 1 ] );
 			} );
 		} );
 	} );
@@ -954,22 +952,6 @@ describe( 'Range', () => {
 		} );
 	} );
 
-	describe( 'isEmpty', () => {
-		beforeEach( () => {
-			prepareRichRoot( root );
-		} );
-
-		it( 'should be true if there are no nodes between range start and end', () => {
-			let range = new Range( new Position( root, [ 0, 0, 5 ] ), new Position( root, [ 0, 1, 0 ] ) );
-			expect( range.isEmpty ).to.be.true;
-		} );
-
-		it( 'should be false if there are nodes between range start and end', () => {
-			let range = new Range( new Position( root, [ 0, 0, 5 ] ), new Position( root, [ 0, 1, 1 ] ) );
-			expect( range.isEmpty ).to.be.false;
-		} );
-	} );
-
 	function mapNodesToNames( nodes ) {
 		return nodes.map( ( node ) => {
 			return ( node instanceof Element ) ? 'E:' + node.name : 'T:' + node.data;