瀏覽代碼

Merge pull request #1509 from ckeditor/t/new-ot-docs

Docs: Updated OT documentation. Closes #1465.
Piotr Jasiun 7 年之前
父節點
當前提交
99d0e993d0

+ 1 - 1
packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js

@@ -84,7 +84,7 @@ const treeDump = Symbol( '_treeDump' );
 // Maximum number of stored states of model and view document.
 // Maximum number of stored states of model and view document.
 const maxTreeDumpLength = 20;
 const maxTreeDumpLength = 20;
 
 
-// Separator used to separate stringified operations
+// Separator used to separate stringified operations.
 const LOG_SEPARATOR = '-------';
 const LOG_SEPARATOR = '-------';
 
 
 // Specified whether debug tools were already enabled.
 // Specified whether debug tools were already enabled.

+ 1 - 1
packages/ckeditor5-engine/src/dev-utils/operationreplayer.js

@@ -4,7 +4,7 @@
  */
  */
 
 
 /**
 /**
- * @module engine/dev-utils/replayer
+ * @module engine/dev-utils/operationreplayer
  */
  */
 
 
 /* global setTimeout */
 /* global setTimeout */

+ 4 - 0
packages/ckeditor5-engine/src/model/differ.js

@@ -119,6 +119,10 @@ export default class Differ {
 	 * @param {module:engine/model/operation/operation~Operation} operation An operation to buffer.
 	 * @param {module:engine/model/operation/operation~Operation} operation An operation to buffer.
 	 */
 	 */
 	bufferOperation( operation ) {
 	bufferOperation( operation ) {
+		// Below we take an operation, check its type, then use its parameters in marking (private) methods.
+		// The general rule is to not mark elements inside inserted element. All inserted elements are re-rendered.
+		// Marking changes in them would cause a "double" changing then.
+		//
 		switch ( operation.type ) {
 		switch ( operation.type ) {
 			case 'insert': {
 			case 'insert': {
 				if ( this._isInInsertedElement( operation.position.parent ) ) {
 				if ( this._isInInsertedElement( operation.position.parent ) ) {

+ 9 - 24
packages/ckeditor5-engine/src/model/liveposition.js

@@ -34,8 +34,7 @@ export default class LivePosition extends Position {
 	 * @see module:engine/model/position~Position
 	 * @see module:engine/model/position~Position
 	 * @param {module:engine/model/rootelement~RootElement} root
 	 * @param {module:engine/model/rootelement~RootElement} root
 	 * @param {Array.<Number>} path
 	 * @param {Array.<Number>} path
-	 * @param {module:engine/model/position~PositionStickiness} [stickiness] Position stickiness. Defaults to `'toNone'`.
-	 * See {@link module:engine/model/liveposition~LivePosition#stickiness}.
+	 * @param {module:engine/model/position~PositionStickiness} [stickiness]
 	 */
 	 */
 	constructor( root, path, stickiness = 'toNone' ) {
 	constructor( root, path, stickiness = 'toNone' ) {
 		super( root, path, stickiness );
 		super( root, path, stickiness );
@@ -51,12 +50,6 @@ export default class LivePosition extends Position {
 			);
 			);
 		}
 		}
 
 
-		/**
-		 * Position stickiness. See {@link module:engine/model/position~PositionStickiness}.
-		 *
-		 * @member {module:engine/model/position~PositionStickiness} module:engine/model/liveposition~LivePosition#stickiness
-		 */
-
 		bindWithDocument.call( this );
 		bindWithDocument.call( this );
 	}
 	}
 
 
@@ -110,14 +103,10 @@ export default class LivePosition extends Position {
 	 */
 	 */
 }
 }
 
 
-/**
- * Binds this `LivePosition` to the {@link module:engine/model/document~Document document} that owns
- * this position's {@link module:engine/model/position~Position#root root}.
- *
- * @ignore
- * @private
- * @method module:engine/model/liveposition~LivePosition.bindWithDocument
- */
+// Binds this `LivePosition` to the {@link module:engine/model/document~Document document} that owns
+// this position's {@link module:engine/model/position~Position#root root}.
+//
+// @private
 function bindWithDocument() {
 function bindWithDocument() {
 	this.listenTo(
 	this.listenTo(
 		this.root.document.model,
 		this.root.document.model,
@@ -135,14 +124,10 @@ function bindWithDocument() {
 	);
 	);
 }
 }
 
 
-/**
- * Updates this position accordingly to the updates applied to the model. Bases on change events.
- *
- * @ignore
- * @private
- * @method transform
- * @param {module:engine/model/operation/operation~Operation} operation Executed operation.
- */
+// Updates this position accordingly to the updates applied to the model. Bases on change events.
+//
+// @private
+// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
 function transform( operation ) {
 function transform( operation ) {
 	const result = this.getTransformedByOperation( operation );
 	const result = this.getTransformedByOperation( operation );
 
 

+ 26 - 27
packages/ckeditor5-engine/src/model/liverange.js

@@ -83,10 +83,9 @@ export default class LiveRange extends Range {
 	 * @event change:range
 	 * @event change:range
 	 * @param {module:engine/model/range~Range} oldRange Range with start and end position equal to start and end position of this live
 	 * @param {module:engine/model/range~Range} oldRange Range with start and end position equal to start and end position of this live
 	 * range before it got changed.
 	 * range before it got changed.
-	 * @param {Object} data Object with additional information about the change. Those parameters are passed from
-	 * {@link module:engine/model/document~Document#event:change document change event}.
-	 * @param {String} data.type Change type.
-	 * @param {module:engine/model/position~Position|null} deletionPosition Source position for move, remove and reinsert change types.
+	 * @param {Object} data Object with additional information about the change.
+	 * @param {module:engine/model/position~Position|null} data.deletionPosition Source position for remove and merge changes.
+	 * Available if the range was moved to the graveyard root, `null` otherwise.
 	 */
 	 */
 
 
 	/**
 	/**
@@ -96,23 +95,16 @@ export default class LiveRange extends Range {
 	 * @event change:content
 	 * @event change:content
 	 * @param {module:engine/model/range~Range} range Range with start and end position equal to start and end position of
 	 * @param {module:engine/model/range~Range} range Range with start and end position equal to start and end position of
 	 * change range.
 	 * change range.
-	 * @param {Object} data Object with additional information about the change. Those parameters are passed from
-	 * {@link module:engine/model/document~Document#event:change document change event}.
-	 * @param {String} data.type Change type.
-	 * @param {module:engine/model/batch~Batch} data.batch Batch which changed the live range.
-	 * @param {module:engine/model/range~Range} data.range Range containing the result of applied change.
-	 * @param {module:engine/model/position~Position} data.sourcePosition Source position for move, remove and reinsert change types.
+	 * @param {Object} data Object with additional information about the change.
+	 * @param {null} data.deletionPosition Due to the nature of this event, this property is always set to `null`. It is passed
+	 * for compatibility with the {@link module:engine/model/liverange~LiveRange#event:change:range} event.
 	 */
 	 */
 }
 }
 
 
-/**
- * Binds this `LiveRange` to the {@link module:engine/model/document~Document document}
- * that owns this range's {@link module:engine/model/range~Range#root root}.
- *
- * @ignore
- * @private
- * @method module:engine/model/liverange~LiveRange#bindWithDocument
- */
+// Binds this `LiveRange` to the {@link module:engine/model/document~Document document}
+// that owns this range's {@link module:engine/model/range~Range#root root}.
+//
+// @private
 function bindWithDocument() {
 function bindWithDocument() {
 	this.listenTo(
 	this.listenTo(
 		this.root.document.model,
 		this.root.document.model,
@@ -130,23 +122,25 @@ function bindWithDocument() {
 	);
 	);
 }
 }
 
 
-/**
- * Updates this range accordingly to the updates applied to the model. Bases on change events.
- *
- * @ignore
- * @private
- * @method transform
- * @param {module:engine/model/operation/operation~Operation} operation Executed operation.
- */
+// Updates this range accordingly to the updates applied to the model. Bases on change events.
+//
+// @private
+// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
 function transform( operation ) {
 function transform( operation ) {
+	// Transform the range by the operation. Join the result ranges if needed.
 	const ranges = this.getTransformedByOperation( operation );
 	const ranges = this.getTransformedByOperation( operation );
 	const result = Range.createFromRanges( ranges );
 	const result = Range.createFromRanges( ranges );
+
 	const boundariesChanged = !result.isEqual( this );
 	const boundariesChanged = !result.isEqual( this );
 	const contentChanged = doesOperationChangeRangeContent( this, operation );
 	const contentChanged = doesOperationChangeRangeContent( this, operation );
+
 	let deletionPosition = null;
 	let deletionPosition = null;
 
 
 	if ( boundariesChanged ) {
 	if ( boundariesChanged ) {
+		// If range boundaries have changed, fire `change:range` event.
+		//
 		if ( result.root.rootName == '$graveyard' ) {
 		if ( result.root.rootName == '$graveyard' ) {
+			// If the range was moved to the graveyard root, set `deletionPosition`.
 			if ( operation.type == 'remove' ) {
 			if ( operation.type == 'remove' ) {
 				deletionPosition = operation.sourcePosition;
 				deletionPosition = operation.sourcePosition;
 			} else {
 			} else {
@@ -155,7 +149,6 @@ function transform( operation ) {
 			}
 			}
 		}
 		}
 
 
-		// If range boundaries have changed, fire `change:range` event.
 		const oldRange = Range.createFromRange( this );
 		const oldRange = Range.createFromRange( this );
 
 
 		this.start = result.start;
 		this.start = result.start;
@@ -168,6 +161,12 @@ function transform( operation ) {
 	}
 	}
 }
 }
 
 
+// Checks whether given operation changes something inside the range (even if it does not change boundaries).
+//
+// @private
+// @param {module:engine/model/range~Range} range Range to check.
+// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
+// @returns {Boolean}
 function doesOperationChangeRangeContent( range, operation ) {
 function doesOperationChangeRangeContent( range, operation ) {
 	switch ( operation.type ) {
 	switch ( operation.type ) {
 		case 'insert':
 		case 'insert':

+ 7 - 2
packages/ckeditor5-engine/src/model/operation/insertoperation.js

@@ -51,8 +51,8 @@ export default class InsertOperation extends Operation {
 
 
 		/**
 		/**
 		 * Flag deciding how the operation should be transformed. If set to `true`, nodes might get additional attributes
 		 * Flag deciding how the operation should be transformed. If set to `true`, nodes might get additional attributes
-		 * during operational transformation. This happens, when operation insertion position points to inside a
-		 * range which attributes have changed.
+		 * during operational transformation. This happens when the operation insertion position is inside of a range
+		 * where attributes have changed.
 		 *
 		 *
 		 * @member {Boolean} module:engine/model/operation/insertoperation~InsertOperation#shouldReceiveAttributes
 		 * @member {Boolean} module:engine/model/operation/insertoperation~InsertOperation#shouldReceiveAttributes
 		 */
 		 */
@@ -66,6 +66,11 @@ export default class InsertOperation extends Operation {
 		return 'insert';
 		return 'insert';
 	}
 	}
 
 
+	/**
+	 * Total offset size of inserted nodes.
+	 *
+	 * @returns {Number}
+	 */
 	get howMany() {
 	get howMany() {
 		return this.nodes.maxOffset;
 		return this.nodes.maxOffset;
 	}
 	}

+ 25 - 12
packages/ckeditor5-engine/src/model/operation/mergeoperation.js

@@ -16,11 +16,12 @@ import { _move } from './utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 /**
 /**
- * Operation to merge two {@link module:engine/model/element~Element elements}. The merged elements are a parent of given
- * {@link module:engine/model/position~Position position} and the next element.
+ * Operation to merge two {@link module:engine/model/element~Element elements}.
  *
  *
- * Technically, the content of the next element is moved at given {@link module:engine/model/position~Position position}
- * and the element is removed.
+ * The merged element is the parent of {@link ~MergeOperation#sourcePosition} and it is merged into the parent of
+ * {@link ~MergeOperation#targetPosition}. All nodes from the merged element are moved to {@link ~MergeOperation#targetPosition}.
+ *
+ * The merged element is moved to the graveyard at {@link ~MergeOperation#graveyardPosition}.
  *
  *
  * @extends module:engine/model/operation/operation~Operation
  * @extends module:engine/model/operation/operation~Operation
  */
  */
@@ -45,7 +46,15 @@ export default class MergeOperation extends Operation {
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#sourcePosition
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#sourcePosition
 		 */
 		 */
 		this.sourcePosition = Position.createFromPosition( sourcePosition );
 		this.sourcePosition = Position.createFromPosition( sourcePosition );
-		this.sourcePosition.stickiness = 'toPrevious'; // This is, and should always remain, the first position in its parent.
+		// This is, and should always remain, the first position in its parent.
+		this.sourcePosition.stickiness = 'toPrevious';
+
+		/**
+		 * Summary offset size of nodes which will be moved from the merged element to the new parent.
+		 *
+		 * @member {Number} module:engine/model/operation/mergeoperation~MergeOperation#howMany
+		 */
+		this.howMany = howMany;
 
 
 		/**
 		/**
 		 * Position which the nodes from the merged elements will be moved to.
 		 * Position which the nodes from the merged elements will be moved to.
@@ -53,12 +62,16 @@ export default class MergeOperation extends Operation {
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#targetPosition
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#targetPosition
 		 */
 		 */
 		this.targetPosition = Position.createFromPosition( targetPosition );
 		this.targetPosition = Position.createFromPosition( targetPosition );
-		this.targetPosition.stickiness = 'toNext'; // This is, and should always remain, the last position in its parent.
-		// is it? think about reversed split operations, undo, etc.
+		// Except of a rare scenario in `MergeOperation` x `MergeOperation` transformation,
+		// this is, and should always remain, the last position in its parent.
+		this.targetPosition.stickiness = 'toNext';
 
 
+		/**
+		 * Position in graveyard to which the merged element will be moved.
+		 *
+		 * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#graveyardPosition
+		 */
 		this.graveyardPosition = Position.createFromPosition( graveyardPosition );
 		this.graveyardPosition = Position.createFromPosition( graveyardPosition );
-
-		this.howMany = howMany;
 	}
 	}
 
 
 	/**
 	/**
@@ -69,7 +82,7 @@ export default class MergeOperation extends Operation {
 	}
 	}
 
 
 	/**
 	/**
-	 * Position before the merged element (which will be removed). Calculated based on the split position.
+	 * Position before the merged element (which will be deleted).
 	 *
 	 *
 	 * @readonly
 	 * @readonly
 	 * @type {module:engine/model/position~Position}
 	 * @type {module:engine/model/position~Position}
@@ -79,8 +92,8 @@ export default class MergeOperation extends Operation {
 	}
 	}
 
 
 	/**
 	/**
-	 * Artificial range that contains all the nodes from the merged element that will be moved to {@link ~#sourcePosition}.
-	 * The range starts at {@link ~#sourcePosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
+	 * Artificial range that contains all the nodes from the merged element that will be moved to {@link ~MergeOperation#sourcePosition}.
+	 * The range starts at {@link ~MergeOperation#sourcePosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
 	 *
 	 *
 	 * @readonly
 	 * @readonly
 	 * @type {module:engine/model/range~Range}
 	 * @type {module:engine/model/range~Range}

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

@@ -42,7 +42,6 @@ export default class MoveOperation extends Operation {
 		 */
 		 */
 		this.sourcePosition = Position.createFromPosition( sourcePosition );
 		this.sourcePosition = Position.createFromPosition( sourcePosition );
 		this.sourcePosition.stickiness = 'toNext';
 		this.sourcePosition.stickiness = 'toNext';
-		// maybe lets change sourcePosition + howMany to a range? flattness will be guaranteed by writer anyway
 
 
 		/**
 		/**
 		 * Offset size of moved range.
 		 * Offset size of moved range.

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

@@ -38,6 +38,12 @@ export default class Operation {
 		 */
 		 */
 		this.isDocumentOperation = this.baseVersion !== null;
 		this.isDocumentOperation = this.baseVersion !== null;
 
 
+		/**
+		 * {@link module:engine/model/batch~Batch Batch} to which the operation is added or `null` if the operation is not
+		 * added to any batch yet.
+		 *
+		 * @member {module:engine/model/batch~Batch|null} #batch
+		 */
 		this.batch = null;
 		this.batch = null;
 
 
 		/**
 		/**
@@ -98,6 +104,7 @@ export default class Operation {
 
 
 		json.__className = this.constructor.className;
 		json.__className = this.constructor.className;
 
 
+		// Remove reference to the parent `Batch` to avoid circular dependencies.
 		delete json.batch;
 		delete json.batch;
 
 
 		// Only document operations are shared with other clients so it is not necessary to keep this information.
 		// Only document operations are shared with other clients so it is not necessary to keep this information.

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

@@ -38,6 +38,7 @@ export default class RenameOperation extends Operation {
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/renameoperation~RenameOperation#position
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/renameoperation~RenameOperation#position
 		 */
 		 */
 		this.position = position;
 		this.position = position;
+		// This position sticks to the next node because it is a position before the node that we want to change.
 		this.position.stickiness = 'toNext';
 		this.position.stickiness = 'toNext';
 
 
 		/**
 		/**

+ 28 - 10
packages/ckeditor5-engine/src/model/operation/splitoperation.js

@@ -16,8 +16,9 @@ import { _insert, _move } from './utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 /**
 /**
- * Operation to split {@link module:engine/model/element~Element an element} at
- * given {@link module:engine/model/position~Position position} into two elements, both containing a part of the element's content.
+ * Operation to split {@link module:engine/model/element~Element an element} at given
+ * {@link module:engine/model/operation/splitoperation~SplitOperation#position position} into two elements,
+ * both containing a part of the element's original content.
  *
  *
  * @extends module:engine/model/operation/operation~Operation
  * @extends module:engine/model/operation/operation~Operation
  */
  */
@@ -26,8 +27,8 @@ export default class SplitOperation extends Operation {
 	 * Creates a split operation.
 	 * Creates a split operation.
 	 *
 	 *
 	 * @param {module:engine/model/position~Position} position Position at which an element should be split.
 	 * @param {module:engine/model/position~Position} position Position at which an element should be split.
-	 * @param {Number}
-	 * @param {module:engine/model/position~Position|null} graveyardPosition Position in graveyard before the element which
+	 * @param {Number} howMany Total offset size of elements that are in the split element after `position`.
+	 * @param {module:engine/model/position~Position|null} graveyardPosition Position in the graveyard root before the element which
 	 * should be used as a parent of the nodes after `position`. If it is not set, a copy of the the `position` parent will be used.
 	 * should be used as a parent of the nodes after `position`. If it is not set, a copy of the the `position` parent will be used.
 	 * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
 	 * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
 	 * can be applied or `null` if the operation operates on detached (non-document) tree.
 	 * can be applied or `null` if the operation operates on detached (non-document) tree.
@@ -41,15 +42,30 @@ export default class SplitOperation extends Operation {
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/splitoperation~SplitOperation#position
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/splitoperation~SplitOperation#position
 		 */
 		 */
 		this.position = Position.createFromPosition( position );
 		this.position = Position.createFromPosition( position );
+		// Keep position sticking to the next node. This way any new content added at the place where the element is split
+		// will be left in the original element.
 		this.position.stickiness = 'toNext';
 		this.position.stickiness = 'toNext';
 
 
+		/**
+		 * Total offset size of elements that are in the split element after `position`.
+		 *
+		 * @member {Number} module:engine/model/operation/splitoperation~SplitOperation#howMany
+		 */
+		this.howMany = howMany;
+
+		/**
+		 * Position in the graveyard root before the element which should be used as a parent of the nodes after `position`.
+		 * If it is not set, a copy of the the `position` parent will be used.
+		 *
+		 * The default behavior is to clone the split element. Element from graveyard is used during undo.
+		 *
+		 * @member {module:engine/model/position~Position|null} #graveyardPosition
+		 */
 		this.graveyardPosition = graveyardPosition ? Position.createFromPosition( graveyardPosition ) : null;
 		this.graveyardPosition = graveyardPosition ? Position.createFromPosition( graveyardPosition ) : null;
 
 
 		if ( this.graveyardPosition ) {
 		if ( this.graveyardPosition ) {
 			this.graveyardPosition.stickiness = 'toNext';
 			this.graveyardPosition.stickiness = 'toNext';
 		}
 		}
-
-		this.howMany = howMany;
 	}
 	}
 
 
 	/**
 	/**
@@ -60,8 +76,9 @@ export default class SplitOperation extends Operation {
 	}
 	}
 
 
 	/**
 	/**
-	 * Position after the split element. This is a position at which the clone of split element will be inserted.
-	 * Calculated based on the split position.
+	 * Position after the split element.
+	 *
+	 * This is a position at which the clone of split element (or element from graveyard) will be inserted.
 	 *
 	 *
 	 * @readonly
 	 * @readonly
 	 * @type {module:engine/model/position~Position}
 	 * @type {module:engine/model/position~Position}
@@ -74,8 +91,9 @@ export default class SplitOperation extends Operation {
 	}
 	}
 
 
 	/**
 	/**
-	 * Position inside the new clone of a split element. This is a position where nodes from after the split position will
-	 * be moved to. Calculated based on the split position.
+	 * Position inside the new clone of a split element.
+	 *
+	 * This is a position where nodes that are after the split position will be moved to.
 	 *
 	 *
 	 * @readonly
 	 * @readonly
 	 * @type {module:engine/model/position~Position}
 	 * @type {module:engine/model/position~Position}

文件差異過大導致無法顯示
+ 1148 - 367
packages/ckeditor5-engine/src/model/operation/transform.js


+ 18 - 6
packages/ckeditor5-engine/src/model/operation/unwrapoperation.js

@@ -26,9 +26,10 @@ export default class UnwrapOperation extends Operation {
 	 * Creates an unwrap operation.
 	 * Creates an unwrap operation.
 	 *
 	 *
 	 * @param {module:engine/model/position~Position} position Position inside the element to unwrap.
 	 * @param {module:engine/model/position~Position} position Position inside the element to unwrap.
-	 * @param {Number} howMany How many nodes are inside unwrapped element.
+	 * @param {Number} howMany Total offset size of nodes that are inside unwrapped element.
+	 * @param {module:engine/model/position~Position} graveyardPosition Position in the graveyard root to which
+	 * the unwrapped element will be moved.
 	 * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
 	 * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
-	 * @param {module:engine/model/position~Position} graveyardPosition Position in graveyard to which the unwrapped element will be moved.
 	 * can be applied or `null` if the operation operates on detached (non-document) tree.
 	 * can be applied or `null` if the operation operates on detached (non-document) tree.
 	 */
 	 */
 	constructor( position, howMany, graveyardPosition, baseVersion ) {
 	constructor( position, howMany, graveyardPosition, baseVersion ) {
@@ -37,19 +38,24 @@ export default class UnwrapOperation extends Operation {
 		/**
 		/**
 		 * Position inside the element to unwrap.
 		 * Position inside the element to unwrap.
 		 *
 		 *
+		 * It should be a position at the beginning of that element.
+		 *
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/unwrapoperation~UnwrapOperation#position
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/unwrapoperation~UnwrapOperation#position
 		 */
 		 */
 		this.position = Position.createFromPosition( position );
 		this.position = Position.createFromPosition( position );
 		this.position.stickiness = 'toPrevious'; // Keep the position always at the beginning of the element.
 		this.position.stickiness = 'toPrevious'; // Keep the position always at the beginning of the element.
 
 
+		/**
+		 * Position in the graveyard root to which the unwrapped element will be moved.
+		 *
+		 * @member {module:engine/model/position~Position} module:engine/model/operation/unwrapoperation~UnwrapOperation#graveyardPosition
+		 */
 		this.graveyardPosition = Position.createFromPosition( graveyardPosition );
 		this.graveyardPosition = Position.createFromPosition( graveyardPosition );
 
 
 		/**
 		/**
-		 * How many nodes are inside unwrapped element.
-		 *
-		 * This information is needed to properly reverse `UnwrapOperation` and to properly transform by `UnwrapOperation`.
+		 * Total offset size of nodes that are inside unwrapped element.
 		 *
 		 *
-		 * @member {Number} module:engine/model/operation/unwrapoperation~UnwrapOperation#_howMany
+		 * @member {Number} module:engine/model/operation/unwrapoperation~UnwrapOperation#howMany
 		 */
 		 */
 		this.howMany = howMany;
 		this.howMany = howMany;
 	}
 	}
@@ -71,6 +77,12 @@ export default class UnwrapOperation extends Operation {
 		return Range.createFromPositionAndShift( this.position, this.howMany );
 		return Range.createFromPositionAndShift( this.position, this.howMany );
 	}
 	}
 
 
+	/**
+	 * A position where the unwrapped nodes will be moved. At the same time, it is the position before the unwrapped element.
+	 *
+	 * @readonly
+	 * @type {module:engine/model/position~Position}
+	 */
 	get targetPosition() {
 	get targetPosition() {
 		const path = this.position.path.slice( 0, -1 );
 		const path = this.position.path.slice( 0, -1 );
 
 

+ 20 - 29
packages/ckeditor5-engine/src/model/operation/utils.js

@@ -123,7 +123,7 @@ export function _move( sourceRange, targetPosition ) {
  *
  *
  * @protected
  * @protected
  * @function module:engine/model/operation/utils~utils._setAttribute
  * @function module:engine/model/operation/utils~utils._setAttribute
- * @param {module:engine/model/range~Range} range Range containing nodes that should have the attribute set. Should be flat.
+ * @param {module:engine/model/range~Range} range Range containing nodes that should have the attribute set. Must be a flat range.
  * @param {String} key Key of attribute to set.
  * @param {String} key Key of attribute to set.
  * @param {*} value Attribute value.
  * @param {*} value Attribute value.
  */
  */
@@ -200,17 +200,14 @@ export function _normalizeNodes( nodes ) {
 	return normalized;
 	return normalized;
 }
 }
 
 
-/**
- * Checks if nodes before and after given index in given element are {@link module:engine/model/text~Text text nodes} and
- * merges them into one node if they have same attributes.
- *
- * Merging is done by removing two text nodes and inserting a new text node containing data from both merged text nodes.
- *
- * @ignore
- * @private
- * @param {module:engine/model/element~Element} element Parent element of nodes to merge.
- * @param {Number} index Index between nodes to merge.
- */
+// Checks if nodes before and after given index in given element are {@link module:engine/model/text~Text text nodes} and
+// merges them into one node if they have same attributes.
+//
+// Merging is done by removing two text nodes and inserting a new text node containing data from both merged text nodes.
+//
+// @private
+// @param {module:engine/model/element~Element} element Parent element of nodes to merge.
+// @param {Number} index Index between nodes to merge.
 function _mergeNodesAtIndex( element, index ) {
 function _mergeNodesAtIndex( element, index ) {
 	const nodeBefore = element.getChild( index - 1 );
 	const nodeBefore = element.getChild( index - 1 );
 	const nodeAfter = element.getChild( index );
 	const nodeAfter = element.getChild( index );
@@ -228,14 +225,11 @@ function _mergeNodesAtIndex( element, index ) {
 	}
 	}
 }
 }
 
 
-/**
- * Checks if given position is in a text node, and if so, splits the text node in two text nodes, each of them
- * containing a part of original text node.
- *
- * @ignore
- * @private
- * @param {module:engine/model/position~Position} position Position at which node should be split.
- */
+// Checks if given position is in a text node, and if so, splits the text node in two text nodes, each of them
+// containing a part of original text node.
+//
+// @private
+// @param {module:engine/model/position~Position} position Position at which node should be split.
 function _splitNodeAtPosition( position ) {
 function _splitNodeAtPosition( position ) {
 	const textNode = position.textNode;
 	const textNode = position.textNode;
 	const element = position.parent;
 	const element = position.parent;
@@ -253,15 +247,12 @@ function _splitNodeAtPosition( position ) {
 	}
 	}
 }
 }
 
 
-/**
- * Checks whether two given nodes have same attributes.
- *
- * @ignore
- * @private
- * @param {module:engine/model/node~Node} nodeA Node to check.
- * @param {module:engine/model/node~Node} nodeB Node to check.
- * @returns {Boolean} `true` if nodes have same attributes, `false` otherwise.
- */
+// Checks whether two given nodes have same attributes.
+//
+// @private
+// @param {module:engine/model/node~Node} nodeA Node to check.
+// @param {module:engine/model/node~Node} nodeB Node to check.
+// @returns {Boolean} `true` if nodes have same attributes, `false` otherwise.
 function _haveSameAttributes( nodeA, nodeB ) {
 function _haveSameAttributes( nodeA, nodeB ) {
 	const iteratorA = nodeA.getAttributes();
 	const iteratorA = nodeA.getAttributes();
 	const iteratorB = nodeB.getAttributes();
 	const iteratorB = nodeB.getAttributes();

+ 15 - 6
packages/ckeditor5-engine/src/model/operation/wrapoperation.js

@@ -17,8 +17,7 @@ import { _insert, _move } from './utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 /**
 /**
- * Operation to wrap a range of {@link module:engine/model/item~Item model items} with
- * a {@link module:engine/model/element~Element model element}.
+ * Operation to wrap a range of {@link module:engine/model/node~Node nodes} with an {@link module:engine/model/element~Element element}.
  *
  *
  * @extends module:engine/model/operation/operation~Operation
  * @extends module:engine/model/operation/operation~Operation
  */
  */
@@ -39,28 +38,38 @@ export default class WrapOperation extends Operation {
 		super( baseVersion );
 		super( baseVersion );
 
 
 		/**
 		/**
-		 * Position before the first {@link module:engine/model/item~Item model item} to wrap.
+		 * Position before the first {@link module:engine/model/node~Node node} to wrap.
 		 *
 		 *
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/wrapoperation~WrapOperation#position
 		 * @member {module:engine/model/position~Position} module:engine/model/operation/wrapoperation~WrapOperation#position
 		 */
 		 */
 		this.position = Position.createFromPosition( position );
 		this.position = Position.createFromPosition( position );
 		this.position.stickiness = 'toNext';
 		this.position.stickiness = 'toNext';
-		// maybe change to a range
 
 
 		/**
 		/**
-		 * Offset size of wrapped range.
+		 * Total offset size of the wrapped range.
+		 *
+		 * Wrapped range will start at `position.offset` and end at `position.offset + howMany`.
 		 *
 		 *
 		 * @member {Number} module:engine/model/operation/wrapoperation~WrapOperation#howMany
 		 * @member {Number} module:engine/model/operation/wrapoperation~WrapOperation#howMany
 		 */
 		 */
 		this.howMany = howMany;
 		this.howMany = howMany;
 
 
 		/**
 		/**
-		 * Element to wrap with.
+		 * Wrapper element that will be used to wrap nodes.
+		 *
+		 * Is `null` if `elementOrPosition` was a {@link module:engine/model/position~Position}.
 		 *
 		 *
 		 * @member {module:engine/model/element~Element} module:engine/model/operation/wrapoperation~WrapOperation#element
 		 * @member {module:engine/model/element~Element} module:engine/model/operation/wrapoperation~WrapOperation#element
 		 */
 		 */
 		this.element = elementOrPosition instanceof Element ? elementOrPosition : null;
 		this.element = elementOrPosition instanceof Element ? elementOrPosition : null;
 
 
+		/**
+		 * Position in the graveyard root before the element that will be used as a wrapper element.
+		 *
+		 * Is `null` if `elementOrPosition` was a {@link module:engine/model/element~Element}.
+		 *
+		 * @member {module:engine/model/element~Element} module:engine/model/operation/wrapoperation~WrapOperation#graveyardPosition
+		 */
 		this.graveyardPosition = elementOrPosition instanceof Element ? null : Position.createFromPosition( elementOrPosition );
 		this.graveyardPosition = elementOrPosition instanceof Element ? null : Position.createFromPosition( elementOrPosition );
 
 
 		if ( this.graveyardPosition ) {
 		if ( this.graveyardPosition ) {

+ 77 - 9
packages/ckeditor5-engine/src/model/position.js

@@ -41,7 +41,7 @@ export default class Position {
 	 *
 	 *
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} root Root of the position.
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} root Root of the position.
 	 * @param {Array.<Number>} path Position path. See {@link module:engine/model/position~Position#path}.
 	 * @param {Array.<Number>} path Position path. See {@link module:engine/model/position~Position#path}.
-	 * @param {module:engine/model/position~PositionStickiness} stickiness Position stickiness. Defaults to `'toNone'`.
+	 * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
 	 * See {@link module:engine/model/position~PositionStickiness}.
 	 * See {@link module:engine/model/position~PositionStickiness}.
 	 */
 	 */
 	constructor( root, path, stickiness = 'toNone' ) {
 	constructor( root, path, stickiness = 'toNone' ) {
@@ -114,10 +114,6 @@ export default class Position {
 		/**
 		/**
 		 * Position stickiness. See {@link module:engine/model/position~PositionStickiness}.
 		 * Position stickiness. See {@link module:engine/model/position~PositionStickiness}.
 		 *
 		 *
-		 * Regular `Position` (not {@link module:engine/model/liveposition~LivePosition}) should always be not-sticky
-		 * (`'toNone'`), unless it is a {@link module:engine/model/range~Range} boundary. Note, that `Range`
-		 * automatically creates positions with correct stickiness.
-		 *
 		 * @member {module:engine/model/position~PositionStickiness} module:engine/model/position~Position#stickiness
 		 * @member {module:engine/model/position~PositionStickiness} module:engine/model/position~Position#stickiness
 		 */
 		 */
 		this.stickiness = stickiness;
 		this.stickiness = stickiness;
@@ -230,6 +226,8 @@ export default class Position {
 	/**
 	/**
 	 * Checks whether this position is before or after given position.
 	 * Checks whether this position is before or after given position.
 	 *
 	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {module:engine/model/position~PositionRelation}
 	 * @returns {module:engine/model/position~PositionRelation}
 	 */
 	 */
@@ -289,7 +287,7 @@ export default class Position {
 	 * Returns a path to this position's parent. Parent path is equal to position {@link module:engine/model/position~Position#path path}
 	 * Returns a path to this position's parent. Parent path is equal to position {@link module:engine/model/position~Position#path path}
 	 * but without the last item.
 	 * but without the last item.
 	 *
 	 *
-	 * This method returns the parent path even if the parent does not exists.
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
 	 *
 	 *
 	 * @returns {Array.<Number>} Path to the parent.
 	 * @returns {Array.<Number>} Path to the parent.
 	 */
 	 */
@@ -314,6 +312,8 @@ export default class Position {
 	 * Returns the slice of two position {@link #path paths} which is identical. The {@link #root roots}
 	 * Returns the slice of two position {@link #path paths} which is identical. The {@link #root roots}
 	 * of these two paths must be identical.
 	 * of these two paths must be identical.
 	 *
 	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
 	 * @param {module:engine/model/position~Position} position The second position.
 	 * @param {module:engine/model/position~Position} position The second position.
 	 * @returns {Array.<Number>} The common path.
 	 * @returns {Array.<Number>} The common path.
 	 */
 	 */
@@ -354,6 +354,8 @@ export default class Position {
 	 * Returns a new instance of `Position`, that has same {@link #parent parent} but it's offset
 	 * Returns a new instance of `Position`, that has same {@link #parent parent} but it's offset
 	 * is shifted by `shift` value (can be a negative value).
 	 * is shifted by `shift` value (can be a negative value).
 	 *
 	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
 	 * @param {Number} shift Offset shift. Can be a negative value.
 	 * @param {Number} shift Offset shift. Can be a negative value.
 	 * @returns {module:engine/model/position~Position} Shifted position.
 	 * @returns {module:engine/model/position~Position} Shifted position.
 	 */
 	 */
@@ -369,8 +371,9 @@ export default class Position {
 	/**
 	/**
 	 * Checks whether this position is after given position.
 	 * Checks whether this position is after given position.
 	 *
 	 *
-	 * @see module:engine/model/position~Position#isBefore
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
 	 *
 	 *
+	 * @see module:engine/model/position~Position#isBefore
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {Boolean} True if this position is after given position.
 	 * @returns {Boolean} True if this position is after given position.
 	 */
 	 */
@@ -406,6 +409,8 @@ export default class Position {
 	 *			// do A.
 	 *			// do A.
 	 *		}
 	 *		}
 	 *
 	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {Boolean} True if this position is before given position.
 	 * @returns {Boolean} True if this position is before given position.
 	 */
 	 */
@@ -416,6 +421,8 @@ export default class Position {
 	/**
 	/**
 	 * Checks whether this position is equal to given position.
 	 * Checks whether this position is equal to given position.
 	 *
 	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {Boolean} True if positions are same.
 	 * @returns {Boolean} True if positions are same.
 	 */
 	 */
@@ -428,8 +435,6 @@ export default class Position {
 	 * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
 	 * 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.
 	 * 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.
 	 * @param {module:engine/model/position~Position} otherPosition Position to compare with.
 	 * @returns {Boolean} True if positions touch.
 	 * @returns {Boolean} True if positions touch.
 	 */
 	 */
@@ -482,6 +487,14 @@ export default class Position {
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Checks if two positions are in the same parent.
+	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
+	 * @param {~Position} position Position to compare with.
+	 * @returns {Boolean} `true` if positions have the same parent, `false` otherwise.
+	 */
 	hasSameParentAs( position ) {
 	hasSameParentAs( position ) {
 		if ( this.root !== position.root ) {
 		if ( this.root !== position.root ) {
 			return false;
 			return false;
@@ -493,6 +506,19 @@ export default class Position {
 		return compareArrays( thisParentPath, posParentPath ) == 'same';
 		return compareArrays( thisParentPath, posParentPath ) == 'same';
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position that is transformed by given `operation`.
+	 *
+	 * The new position's parameters are updated accordingly to the effect of the `operation`.
+	 *
+	 * For example, if `n` nodes are inserted before the position, the returned position {@link ~Position#offset} will be
+	 * increased by `n`. If the position was in a merged element, it will be accordingly moved to the new element, etc.
+	 *
+	 * This method is safe to use it on non-existing positions (for example during operational transformation).
+	 *
+	 * @param {module:engine/model/operation/operation~Operation} operation Operation to transform by.
+	 * @returns {module:engine/model/position~Position} Transformed position.
+	 */
 	getTransformedByOperation( operation ) {
 	getTransformedByOperation( operation ) {
 		let result;
 		let result;
 
 
@@ -525,14 +551,35 @@ export default class Position {
 		return result;
 		return result;
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by an insert operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/insertoperation~InsertOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedByInsertOperation( operation ) {
 	_getTransformedByInsertOperation( operation ) {
 		return this._getTransformedByInsertion( operation.position, operation.howMany );
 		return this._getTransformedByInsertion( operation.position, operation.howMany );
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by a move operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/moveoperation~MoveOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedByMoveOperation( operation ) {
 	_getTransformedByMoveOperation( operation ) {
 		return this._getTransformedByMove( operation.sourcePosition, operation.targetPosition, operation.howMany );
 		return this._getTransformedByMove( operation.sourcePosition, operation.targetPosition, operation.howMany );
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by a split operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/splitoperation~SplitOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedBySplitOperation( operation ) {
 	_getTransformedBySplitOperation( operation ) {
 		const movedRange = operation.movedRange;
 		const movedRange = operation.movedRange;
 
 
@@ -550,6 +597,13 @@ export default class Position {
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by merge operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/mergeoperation~MergeOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedByMergeOperation( operation ) {
 	_getTransformedByMergeOperation( operation ) {
 		const movedRange = operation.movedRange;
 		const movedRange = operation.movedRange;
 		const isContained = movedRange.containsPosition( this ) || movedRange.start.isEqual( this );
 		const isContained = movedRange.containsPosition( this ) || movedRange.start.isEqual( this );
@@ -572,6 +626,13 @@ export default class Position {
 		return pos;
 		return pos;
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by wrap operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/wrapoperation~WrapOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedByWrapOperation( operation ) {
 	_getTransformedByWrapOperation( operation ) {
 		const wrappedRange = operation.wrappedRange;
 		const wrappedRange = operation.wrappedRange;
 
 
@@ -594,6 +655,13 @@ export default class Position {
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this position transformed by unwrap operation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/unwrapoperation~UnwrapOperation} operation
+	 * @returns {module:engine/model/position~Position}
+	 */
 	_getTransformedByUnwrapOperation( operation ) {
 	_getTransformedByUnwrapOperation( operation ) {
 		const unwrappedRange = operation.unwrappedRange;
 		const unwrappedRange = operation.unwrappedRange;
 
 

+ 66 - 5
packages/ckeditor5-engine/src/model/range.js

@@ -41,6 +41,8 @@ export default class Range {
 		 */
 		 */
 		this.end = end ? Position.createFromPosition( end ) : Position.createFromPosition( start );
 		this.end = end ? Position.createFromPosition( end ) : Position.createFromPosition( start );
 
 
+		// If the range is collapsed, treat in a similar way as a position and set its boundaries stickiness to 'toNone'.
+		// In other case, make the boundaries stick to the "inside" of the range.
 		this.start.stickiness = this.isCollapsed ? 'toNone' : 'toNext';
 		this.start.stickiness = this.isCollapsed ? 'toNone' : 'toNext';
 		this.end.stickiness = this.isCollapsed ? 'toNone' : 'toPrevious';
 		this.end.stickiness = this.isCollapsed ? 'toNone' : 'toPrevious';
 	}
 	}
@@ -421,10 +423,7 @@ export default class Range {
 	/**
 	/**
 	 * Returns a range that is a result of transforming this range by multiple `operations`.
 	 * Returns a range that is a result of transforming this range by multiple `operations`.
 	 *
 	 *
-	 * **Note:** transformation may break one range into multiple ranges (e.g. when a part of the range is
-	 * moved to a different part of document tree). For this reason, an array is returned by this method and it
-	 * may contain one or more `Range` instances.
-	 *
+	 * @see ~Range#getTransformedByOperation
 	 * @param {Iterable.<module:engine/model/operation/operation~Operation>} operations Operations to transform the range by.
 	 * @param {Iterable.<module:engine/model/operation/operation~Operation>} operations Operations to transform the range by.
 	 * @returns {Array.<module:engine/model/range~Range>} Range which is the result of transformation.
 	 * @returns {Array.<module:engine/model/range~Range>} Range which is the result of transformation.
 	 */
 	 */
@@ -481,12 +480,26 @@ export default class Range {
 		};
 		};
 	}
 	}
 
 
+	/**
+	 * Returns a result of transforming a copy of this range by insert operation.
+	 *
+	 * One or more ranges may be returned as a result of this transformation.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/insertoperation~InsertOperation} operation
+	 * @returns {Array.<module:engine/model/range~Range>}
+	 */
 	_getTransformedByInsertOperation( operation, spread = false ) {
 	_getTransformedByInsertOperation( operation, spread = false ) {
 		return this._getTransformedByInsertion( operation.position, operation.howMany, spread );
 		return this._getTransformedByInsertion( operation.position, operation.howMany, spread );
 	}
 	}
 
 
 	/**
 	/**
+	 * Returns a result of transforming a copy of this range by move operation.
+	 *
+	 * One or more ranges may be returned as a result of this transformation.
+	 *
 	 * @protected
 	 * @protected
+	 * @param {module:engine/model/operation/moveoperation~MoveOperation} operation
 	 * @returns {Array.<module:engine/model/range~Range>}
 	 * @returns {Array.<module:engine/model/range~Range>}
 	 */
 	 */
 	_getTransformedByMoveOperation( operation, spread = false ) {
 	_getTransformedByMoveOperation( operation, spread = false ) {
@@ -497,6 +510,15 @@ export default class Range {
 		return this._getTransformedByMove( sourcePosition, targetPosition, howMany, spread );
 		return this._getTransformedByMove( sourcePosition, targetPosition, howMany, spread );
 	}
 	}
 
 
+	/**
+	 * Returns a result of transforming a copy of this range by split operation.
+	 *
+	 * Always one range is returned. The transformation is done in a way to not break the range.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/splitoperation~SplitOperation} operation
+	 * @returns {module:engine/model/range~Range}
+	 */
 	_getTransformedBySplitOperation( operation ) {
 	_getTransformedBySplitOperation( operation ) {
 		const start = this.start._getTransformedBySplitOperation( operation );
 		const start = this.start._getTransformedBySplitOperation( operation );
 		const end = this.end._getTransformedBySplitOperation( operation );
 		const end = this.end._getTransformedBySplitOperation( operation );
@@ -504,6 +526,15 @@ export default class Range {
 		return new Range( start, end );
 		return new Range( start, end );
 	}
 	}
 
 
+	/**
+	 * Returns a result of transforming a copy of this range by merge operation.
+	 *
+	 * Always one range is returned. The transformation is done in a way to not break the range.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/mergeoperation~MergeOperation} operation
+	 * @returns {module:engine/model/range~Range}
+	 */
 	_getTransformedByMergeOperation( operation ) {
 	_getTransformedByMergeOperation( operation ) {
 		let start = this.start._getTransformedByMergeOperation( operation );
 		let start = this.start._getTransformedByMergeOperation( operation );
 		let end = this.end._getTransformedByMergeOperation( operation );
 		let end = this.end._getTransformedByMergeOperation( operation );
@@ -553,6 +584,15 @@ export default class Range {
 		return new Range( start, end );
 		return new Range( start, end );
 	}
 	}
 
 
+	/**
+	 * Returns a result of transforming a copy of this range by wrap operation.
+	 *
+	 * Always one range is returned. The transformation is done in a way to not break the range.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/wrapoperation~WrapOperation} operation
+	 * @returns {module:engine/model/range~Range}
+	 */
 	_getTransformedByWrapOperation( operation ) {
 	_getTransformedByWrapOperation( operation ) {
 		const start = this.start._getTransformedByWrapOperation( operation );
 		const start = this.start._getTransformedByWrapOperation( operation );
 		const end = this.end._getTransformedByWrapOperation( operation );
 		const end = this.end._getTransformedByWrapOperation( operation );
@@ -560,6 +600,15 @@ export default class Range {
 		return new Range( start, end );
 		return new Range( start, end );
 	}
 	}
 
 
+	/**
+	 * Returns a result of transforming a copy of this range by unwrap operation.
+	 *
+	 * Always one range is returned. The transformation is done in a way to not break the range.
+	 *
+	 * @protected
+	 * @param {module:engine/model/operation/unwrapoperation~UnwrapOperation} operation
+	 * @returns {module:engine/model/range~Range}
+	 */
 	_getTransformedByUnwrapOperation( operation ) {
 	_getTransformedByUnwrapOperation( operation ) {
 		const start = this.start._getTransformedByUnwrapOperation( operation );
 		const start = this.start._getTransformedByUnwrapOperation( operation );
 		const end = this.end._getTransformedByUnwrapOperation( operation );
 		const end = this.end._getTransformedByUnwrapOperation( operation );
@@ -625,7 +674,7 @@ export default class Range {
 	 * @param {module:engine/model/position~Position} sourcePosition Position from which nodes are moved.
 	 * @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 {module:engine/model/position~Position} targetPosition Position to where nodes are moved.
 	 * @param {Number} howMany How many nodes are moved.
 	 * @param {Number} howMany How many nodes are moved.
-	 * @param {Boolean} [spread=false]
+	 * @param {Boolean} [spread=false] Whether the range should be spread if the move points inside the range.
 	 * @returns {Array.<module:engine/model/range~Range>} Result of the transformation.
 	 * @returns {Array.<module:engine/model/range~Range>} Result of the transformation.
 	 */
 	 */
 	_getTransformedByMove( sourcePosition, targetPosition, howMany, spread = false ) {
 	_getTransformedByMove( sourcePosition, targetPosition, howMany, spread = false ) {
@@ -706,6 +755,18 @@ export default class Range {
 		return result;
 		return result;
 	}
 	}
 
 
+	/**
+	 * Returns a copy of this range that is transformed by deletion of `howMany` nodes from `deletePosition`.
+	 *
+	 * If the deleted range is intersecting with the transformed range, the transformed range will be shrank.
+	 *
+	 * If the deleted range contains transformed range, `null` will be returned.
+	 *
+	 * @protected
+	 * @param {module:engine/model/position~Position} deletionPosition Position from which nodes are removed.
+	 * @param {Number} howMany How many nodes are removed.
+	 * @returns {module:engine/model/range~Range|null} Result of the transformation.
+	 */
 	_getTransformedByDeletion( deletePosition, howMany ) {
 	_getTransformedByDeletion( deletePosition, howMany ) {
 		let newStart = this.start._getTransformedByDeletion( deletePosition, howMany );
 		let newStart = this.start._getTransformedByDeletion( deletePosition, howMany );
 		let newEnd = this.end._getTransformedByDeletion( deletePosition, howMany );
 		let newEnd = this.end._getTransformedByDeletion( deletePosition, howMany );

+ 1 - 1
packages/ckeditor5-engine/src/model/treewalker.js

@@ -90,7 +90,7 @@ export default class TreeWalker {
 			this.position = Position.createFromPosition( this.boundaries[ this.direction == 'backward' ? 'end' : 'start' ] );
 			this.position = Position.createFromPosition( this.boundaries[ this.direction == 'backward' ? 'end' : 'start' ] );
 		}
 		}
 
 
-		// Reset position stickiness in case it was set to other value.
+		// Reset position stickiness in case it was set to other value, as the stickiness is kept after cloning.
 		this.position.stickiness = 'toNone';
 		this.position.stickiness = 'toNone';
 
 
 		/**
 		/**

+ 4 - 1
packages/ckeditor5-engine/src/model/writer.js

@@ -543,6 +543,7 @@ export default class Writer {
 	 * Performs merge action in a detached tree.
 	 * Performs merge action in a detached tree.
 	 *
 	 *
 	 * @private
 	 * @private
+	 * @param {module:engine/model/position~Position} position Position between merged elements.
 	 */
 	 */
 	_mergeDetached( position ) {
 	_mergeDetached( position ) {
 		const nodeBefore = position.nodeBefore;
 		const nodeBefore = position.nodeBefore;
@@ -1222,6 +1223,8 @@ export default class Writer {
 // Because attribute operation needs to have the same attribute value on the whole range, this function splits
 // Because attribute operation needs to have the same attribute value on the whole range, this function splits
 // the range into smaller parts.
 // the range into smaller parts.
 //
 //
+// Given `range` must be flat.
+//
 // @private
 // @private
 // @param {module:engine/model/writer~Writer} writer
 // @param {module:engine/model/writer~Writer} writer
 // @param {String} key Attribute key.
 // @param {String} key Attribute key.
@@ -1336,7 +1339,7 @@ function applyMarkerOperation( writer, name, oldRange, newRange, affectsData ) {
 // @private
 // @private
 // @param {module:engine/model/position~Position} position Position from which nodes are removed.
 // @param {module:engine/model/position~Position} position Position from which nodes are removed.
 // @param {Number} howMany Number of nodes to remove.
 // @param {Number} howMany Number of nodes to remove.
-// @param {Batch} batch
+// @param {Batch} batch Batch to which the operation will be added.
 // @param {module:engine/model/model~Model} model Model instance on which operation will be applied.
 // @param {module:engine/model/model~Model} model Model instance on which operation will be applied.
 function applyRemoveOperation( position, howMany, batch, model ) {
 function applyRemoveOperation( position, howMany, batch, model ) {
 	let operation;
 	let operation;