Przeglądaj źródła

Docs: huge docs refactor in engine, also changed methods order and variables names.

Szymon Cofalik 9 lat temu
rodzic
commit
215386d4dd
36 zmienionych plików z 610 dodań i 451 usunięć
  1. 0 0
      packages/ckeditor5-engine/src/conversion/conversion.jsdoc
  2. 10 10
      packages/ckeditor5-engine/src/conversion/mapper.js
  3. 7 7
      packages/ckeditor5-engine/src/conversion/model-converter-builder.js
  4. 14 13
      packages/ckeditor5-engine/src/conversion/model-selection-to-view-converters.js
  5. 1 1
      packages/ckeditor5-engine/src/conversion/model-to-view-converters.js
  6. 1 1
      packages/ckeditor5-engine/src/conversion/view-converter-builder.js
  7. 9 10
      packages/ckeditor5-engine/src/conversion/view-selection-to-model-converters.js
  8. 4 4
      packages/ckeditor5-engine/src/conversion/view-to-model-converters.js
  9. 7 8
      packages/ckeditor5-engine/src/model/batch.js
  10. 33 27
      packages/ckeditor5-engine/src/model/delta/attributedelta.js
  11. 9 0
      packages/ckeditor5-engine/src/model/delta/insertdelta.js
  12. 10 4
      packages/ckeditor5-engine/src/model/delta/mergedelta.js
  13. 16 9
      packages/ckeditor5-engine/src/model/delta/movedelta.js
  14. 6 6
      packages/ckeditor5-engine/src/model/delta/removedelta.js
  15. 4 1
      packages/ckeditor5-engine/src/model/delta/renamedelta.js
  16. 18 8
      packages/ckeditor5-engine/src/model/delta/splitdelta.js
  17. 1 1
      packages/ckeditor5-engine/src/model/delta/transform.js
  18. 7 1
      packages/ckeditor5-engine/src/model/delta/unwrapdelta.js
  19. 6 6
      packages/ckeditor5-engine/src/model/delta/weakinsertdelta.js
  20. 12 3
      packages/ckeditor5-engine/src/model/delta/wrapdelta.js
  21. 6 5
      packages/ckeditor5-engine/src/model/document.js
  22. 3 3
      packages/ckeditor5-engine/src/model/history.js
  23. 14 11
      packages/ckeditor5-engine/src/model/liveposition.js
  24. 8 9
      packages/ckeditor5-engine/src/model/liverange.js
  25. 11 8
      packages/ckeditor5-engine/src/model/liveselection.js
  26. 22 12
      packages/ckeditor5-engine/src/model/operation/attributeoperation.js
  27. 10 2
      packages/ckeditor5-engine/src/model/operation/insertoperation.js
  28. 17 9
      packages/ckeditor5-engine/src/model/operation/moveoperation.js
  29. 7 3
      packages/ckeditor5-engine/src/model/operation/nooperation.js
  30. 1 1
      packages/ckeditor5-engine/src/model/operation/operationfactory.js
  31. 11 9
      packages/ckeditor5-engine/src/model/operation/reinsertoperation.js
  32. 12 7
      packages/ckeditor5-engine/src/model/operation/removeoperation.js
  33. 2 4
      packages/ckeditor5-engine/src/model/operation/transform.js
  34. 59 37
      packages/ckeditor5-engine/src/model/position.js
  35. 85 77
      packages/ckeditor5-engine/src/model/range.js
  36. 167 134
      packages/ckeditor5-engine/src/model/selection.js

+ 0 - 0
packages/ckeditor5-engine/src/conversion/treecontroller.jsdoc → packages/ckeditor5-engine/src/conversion/conversion.jsdoc


+ 10 - 10
packages/ckeditor5-engine/src/conversion/mapper.js

@@ -14,8 +14,8 @@ import ViewText from '../view/text.js';
 /**
  * Maps elements and positions between {@link engine.view.Document view} and {@link engine.model model}.
  *
- * Mapper use binded elements to find corresponding elements and positions, so, to get proper results,
- * all Tree Model elements should be {@link engine.conversion.Mapper#bindElements binded}.
+ * Mapper use bound elements to find corresponding elements and positions, so, to get proper results,
+ * all model elements should be {@link engine.conversion.Mapper#bindElements bound}.
  *
  * @memberOf engine.conversion
  */
@@ -25,7 +25,7 @@ export default class Mapper {
 	 */
 	constructor() {
 		/**
-		 * Model element to View element mapping.
+		 * Model element to view element mapping.
 		 *
 		 * @private
 		 * @member {WeakMap} engine.conversion.Mapper#_modelToViewMapping
@@ -33,7 +33,7 @@ export default class Mapper {
 		this._modelToViewMapping = new WeakMap();
 
 		/**
-		 * View element to Model element mapping.
+		 * View element to model element mapping.
 		 *
 		 * @private
 		 * @member {WeakMap} engine.conversion.Mapper#_viewToModelMapping
@@ -67,7 +67,7 @@ export default class Mapper {
 	 * Gets the corresponding model element.
 	 *
 	 * @param {engine.view.Element} viewElement View element.
-	 * @returns {engine.model.Element|null} Corresponding model element or `null` if not found.
+	 * @returns {engine.model.Element|undefined} Corresponding model element or `undefined` if not found.
 	 */
 	toModelElement( viewElement ) {
 		return this._viewToModelMapping.get( viewElement );
@@ -77,7 +77,7 @@ export default class Mapper {
 	 * Gets the corresponding view element.
 	 *
 	 * @param {engine.model.Element} modelElement Model element.
-	 * @returns {engine.view.Element|null} Corresponding view element or `null` if not found.
+	 * @returns {engine.view.Element|undefined} Corresponding view element or `undefined` if not found.
 	 */
 	toViewElement( modelElement ) {
 		return this._modelToViewMapping.get( modelElement );
@@ -156,10 +156,10 @@ export default class Mapper {
 	_toModelOffset( viewParent, viewOffset, viewBlock ) {
 		if ( viewBlock != viewParent ) {
 			// See example.
-			const offsetToParentBegging = this._toModelOffset( viewParent.parent, viewParent.getIndex(), viewBlock );
+			const offsetToParentStart = this._toModelOffset( viewParent.parent, viewParent.getIndex(), viewBlock );
 			const offsetInParent = this._toModelOffset( viewParent, viewOffset, viewParent );
 
-			return offsetToParentBegging + offsetInParent;
+			return offsetToParentStart + offsetInParent;
 		}
 
 		// viewBlock == viewParent, so we need to calculate the offset in the parent element.
@@ -185,8 +185,8 @@ export default class Mapper {
 	 * Examples:
 	 *
 	 *		foo          -> 3 // Length of the text is the length of data.
-	 *		<b>foo</b>   -> 3 // Length the element which has no corresponding model element is a length of its children.
-	 *		<p>foo</p>   -> 1 // Length the element which has corresponding model element is always 1.
+	 *		<b>foo</b>   -> 3 // Length of the element which has no corresponding model element is a length of its children.
+	 *		<p>foo</p>   -> 1 // Length of the element which has corresponding model element is always 1.
 	 *
 	 * @private
 	 * @param {engine.view.Element} viewNode View node.

+ 7 - 7
packages/ckeditor5-engine/src/conversion/model-converter-builder.js

@@ -42,9 +42,9 @@ import ViewContainerElement from '../view/containerelement.js';
  *		BuildModelConverterFor( dispatcher ).fromAttribute( 'src' ).toAttribute();
  *
  * 3. Model attribute to view element converter. This is a converter that takes model attributes and represents them
- * as view elements. Those view elements are wrapping view elements are node that correspond to model elements and
- * nodes which had converter attribute. It is suitable for attributes like `bold`, where `bold` attribute set on model
- * text nodes is converter to `strong` view element.
+ * as view elements. Elements created by this kind of converter are wrapping other view elements. Wrapped view nodes
+ * correspond to model nodes had converter attribute. It is suitable for attributes like `bold`, where `bold` attribute
+ * set on model text nodes is converter to `strong` view element.
  *
  *		BuildModelConverterFor( dispatcher ).fromAttribute( 'bold' ).toElement( 'strong' );
  *
@@ -52,8 +52,8 @@ import ViewContainerElement from '../view/containerelement.js';
  * and {@link engine.conversion.ModelConverterBuilder#toAttribute} methods. See their descriptions to learn more.
  *
  * It is also possible to {@link engine.conversion.ModelConverterBuilder#withPriority change default priority}
- * of created converters to decide which converter should be fired earlier and which later. This is useful if you provide
- * a general converter but want to provide different converter for a specific-case (i.e. given model element is converted
+ * of created converters to decide which converter should be fired earlier and which later. This is useful if you have
+ * a general converter but also want to provide different special-case converters (i.e. given model element is converted
  * always to given view element, but if it has given attribute it is converter to other view element). For this,
  * use {@link engine.conversion.ModelConverterBuilder#withPriority withPriority} right after `from...` method.
  *
@@ -127,8 +127,8 @@ class ModelConverterBuilder {
 	 * Default priority is `10`.
 	 *
 	 * **Note:** Keep in mind that event priority, that is set by this modifier, is used for attribute priority
-	 * when {@link engine.view.writer} is used. This changes how model attributes converter to view elements are
-	 * ordered, i.e.: `<strong><em>foo</em></strong>` vs `<em><strong>foo</strong></em>`. Using priority you can also
+	 * when {@link engine.view.writer} is used. This changes how view elements are ordered,
+	 * i.e.: `<strong><em>foo</em></strong>` vs `<em><strong>foo</strong></em>`. Using priority you can also
 	 * prevent node merging, i.e.: `<span class="bold"><span class="theme">foo</span><span>` vs `<span class="bold theme">foo</span>`.
 	 * If you want to prevent merging, just set different priority for both converters.
 	 *

+ 14 - 13
packages/ckeditor5-engine/src/conversion/model-selection-to-view-converters.js

@@ -58,15 +58,15 @@ export function convertRangeSelection() {
  *
  * Example of view state before and after converting collapsed selection:
  *
- *		<p><strong>f^oo<strong>bar</p>
+ *		   <p><strong>f^oo<strong>bar</p>
  *		-> <p><strong>f</strong>^<strong>oo</strong>bar</p>
  *
- * By breaking attribute elements like `<strong>` selection is in correct elements. See also complementary
+ * By breaking attribute elements like `<strong>`, selection is in correct element. See also complementary
  * {@link engine.conversion.modelSelectionToView.convertSelectionAttribute attribute converter} for selection attributes,
  * which wraps collapsed selection into view elements. Those converters together ensure, that selection ends up in
- * appropriate elements.
+ * appropriate attribute elements.
  *
- * See also {@link engine.conversion.modelSelectionToView.clearAttributes} which do the clean-up by merging attributes.
+ * See also {@link engine.conversion.modelSelectionToView.clearAttributes} which does a clean-up by merging attributes.
  *
  * @external engine.conversion.modelSelectionToView
  * @function engine.conversion.modelSelectionToView.convertCollapsedSelection
@@ -118,20 +118,21 @@ export function convertCollapsedSelection() {
  * **Note:** You can use the same `elementCreator` function for this converter factory and {@link engine.conversion.modelToView.wrap}
  * model to view converter, as long as the `elementCreator` function uses only the first parameter (attribute value).
  *
- * Example of view state after converting collapsed selection. The scenario is: selection is inside bold text (`<strong>` element)
- * but it does not have bold attribute itself, but has italic attribute instead (let's assume that user turned off bold and turned
- * on italic with selection collapsed):
- *
  *		modelDispatcher.on( 'selection', convertCollapsedSelection() );
  *		modelDispatcher.on( 'selectionAttribute:italic', convertSelectionAttribute( new ViewAttributeElement( 'em' ) ) );
+ *		modelDispatcher.on( 'selectionAttribute:bold', convertSelectionAttribute( new ViewAttributeElement( 'strong' ) ) );
  *
  * Example of view states before and after converting collapsed selection:
  *
- *		<p><em>f^oo</em>bar</p>
+ *		   <p><em>f^oo</em>bar</p>
  *		-> <p><em>f</em>^<em>oo</em>bar</p>
  *		-> <p><em>f^oo</em>bar</p>
  *
- *		<p><strong>f^oo<strong>bar</p>
+ * Example of view state after converting collapsed selection. The scenario is: selection is inside bold text (`<strong>` element)
+ * but it does not have bold attribute itself and has italic attribute instead (let's assume that user turned off bold and turned
+ * on italic with selection collapsed):
+ *
+ *		   <p><strong>f^oo<strong>bar</p>
  *		-> <p><strong>f</strong>^<strong>oo<strong>bar</p>
  *		-> <p><strong>f</strong><em>^</em><strong>oo</strong>bar</p>
  *
@@ -176,13 +177,13 @@ export function convertSelectionAttribute( elementCreator ) {
  * {@link engine.view.AttributeElement view attribute elements} and merge sibling attributes at all start and end
  * positions of all ranges.
  *
- *		<p><strong>^</strong></p>
+ *		   <p><strong>^</strong></p>
  *		-> <p>^</p>
  *
- *		<p><strong>foo</strong>^<strong>bar</strong>bar</p>
+ *		   <p><strong>foo</strong>^<strong>bar</strong>bar</p>
  *		-> <p><strong>foo^bar<strong>bar</p>
  *
- *		<p><strong>foo</strong><em>^</em><strong>bar</strong>bar</p>
+ *		   <p><strong>foo</strong><em>^</em><strong>bar</strong>bar</p>
  *		-> <p><strong>foo^bar<strong>bar</p>
  *
  * This listener should be assigned before any converter for the new selection:

+ 1 - 1
packages/ckeditor5-engine/src/conversion/model-to-view-converters.js

@@ -185,7 +185,7 @@ export function removeAttribute( attributeCreator ) {
 /**
  * Function factory, creates a converter that converts set/change attribute changes from the model to the view. In this case,
  * model attributes are converted to a view element that will be wrapping view nodes which corresponding model nodes had
- * the attribute set. This is useful for attributes like `bold`, which may be set on a text nodes in model but are
+ * the attribute set. This is useful for attributes like `bold`, which may be set on text nodes in model but are
  * represented as an element in the view:
  *
  *		[paragraph]              MODEL ====> VIEW        <p>

+ 1 - 1
packages/ckeditor5-engine/src/conversion/view-converter-builder.js

@@ -374,7 +374,7 @@ class ViewConverterBuilder {
 	}
 }
 
-// Helper function that sets given attributes on given `engine.model.Item` or `engine.model.DocumentFragment`.
+// Helper function that sets given attributes on given `engine.model.Node` or `engine.model.DocumentFragment`.
 function setAttributeOn( toChange, attribute, data, conversionApi ) {
 	if ( isIterable( toChange ) ) {
 		for ( let node of toChange ) {

+ 9 - 10
packages/ckeditor5-engine/src/conversion/view-selection-to-model-converters.js

@@ -14,29 +14,28 @@
 
 /**
  * Function factory, creates a callback function which converts a {@link engine.view.Selection view selection} taken
- * from the {@link engine.view.Document#selectionChange} event and set in on the
- * {@link engine.model.Document#selection model}.
+ * from the {@link engine.view.Document#selectionChange} event and sets in on the {@link engine.model.Document#selection model}.
  *
- * Note that because there is not view selection change dispatcher nor any other advance view selection to model
- * conversion mechanism, this method is simple event listener.
+ * **Note**: because there is no view selection change dispatcher nor any other advanced view selection to model
+ * conversion mechanism, the callback should be set directly on view document.
  *
- *		view.document.on( 'selectionChange', convertSelectionChange( model, mapper ) );
+ *		view.document.on( 'selectionChange', convertSelectionChange( modelDocument, mapper ) );
  *
  * @function engine.conversion.viewSelectionToModel.convertSelectionChange
- * @param {engine.model.Document} model Document model on which selection should be updated.
+ * @param {engine.model.Document} modelDocument Model document on which selection should be updated.
  * @param {engine.conversion.Mapper} mapper Conversion mapper.
  * @returns {Function} {@link engine.view.Document#selectionChange} callback function.
  */
-export function convertSelectionChange( model, mapper ) {
+export function convertSelectionChange( modelDocument, mapper ) {
 	return ( evt, data ) => {
-		model.enqueueChanges( () => {
+		modelDocument.enqueueChanges( () => {
 			const viewSelection = data.newSelection;
 
-			model.selection.removeAllRanges();
+			modelDocument.selection.removeAllRanges();
 
 			for ( let viewRange of viewSelection.getRanges() ) {
 				const modelRange = mapper.toModelRange( viewRange );
-				model.selection.addRange( modelRange, viewSelection.isBackward );
+				modelDocument.selection.addRange( modelRange, viewSelection.isBackward );
 			}
 		} );
 	};

+ 4 - 4
packages/ckeditor5-engine/src/conversion/view-to-model-converters.js

@@ -19,12 +19,12 @@ import { normalizeNodes } from '../model/writer.js';
 /**
  * Function factory, creates a converter that converts {@link engine.view.DocumentFragment view document fragment} or
  * all children of {@link engine.view.Element} into {@link engine.model.DocumentFragment model document fragment}.
- * This is the "entry-point" converter for view to model conversion. This converter starts the conversion of all "children"
- * of passed view document fragment. Those "children" {@link engine.view.Node view nodes} are then handled by other converters.
+ * This is the "entry-point" converter for view to model conversion. This converter starts the conversion of all children
+ * of passed view document fragment. Those children {@link engine.view.Node view nodes} are then handled by other converters.
  *
  * This also a "default", last resort converter for all view elements that has not been converted by other converters.
- * When a view element is converted to the model and it does not have it's converter specified, all of that elements
- * children will be converted to {@link engine.model.DocumentFragment} and returned.
+ * When a view element is being converted to the model but it does not have converter specified, that view element
+ * will be converted to {@link engine.model.DocumentFragment model document fragment} and returned.
  *
  * @external engine.conversion.viewToModel
  * @function engine.conversion.viewToModel.convertToModelFragment

+ 7 - 8
packages/ckeditor5-engine/src/model/batch.js

@@ -8,10 +8,9 @@
 import CKEditorError from '../../utils/ckeditorerror.js';
 
 /**
- * The Batch class groups document changes (deltas). All deltas grouped in a single Batch can be
- * reverted together, so you can think about the Batch as a single undo step. If you want to extend one
- * undo step you can call another method on the same Batch object. If you want to create a separate undo step
- * you can create a new Batch.
+ * `Batch` instance groups document changes ({@link engine.model.Delta deltas}). All deltas grouped in a single `Batch` can be
+ * reverted together, so you can think about `Batch` as of a single undo step. If you want to extend given undo step you
+ * can call another method on the same `Batch` object. If you want to create a separate undo step you can create a new `Batch`.
  *
  * For example to create two separate undo steps you can call:
  *
@@ -32,7 +31,7 @@ import CKEditorError from '../../utils/ckeditorerror.js';
  */
 export default class Batch {
 	/**
-	 * Creates Batch instance. Not recommended to use directly, use {@link engine.model.Document#batch} instead.
+	 * Creates `Batch` instance. Not recommended to use directly, use {@link engine.model.Document#batch} instead.
 	 *
 	 * @param {engine.model.Document} document Document which this Batch changes.
 	 * @param {'transparent'|'default'} [type='default'] Type of the batch.
@@ -105,7 +104,7 @@ export default class Batch {
 }
 
 /**
- * Function to register batch methods. To make code scalable Batch do not have modification
+ * Function to register batch methods. To make code scalable `Batch` do not have modification
  * methods built in. They can be registered using this method.
  *
  * This method checks if there is no naming collision and throws `batch-register-taken` if the method name
@@ -116,10 +115,10 @@ export default class Batch {
  * For example:
  *
  *		Batch.register( 'insert', function( position, nodes ) {
- *			// You can use a class inherit from Delta if that class should handle OT in the special way.
+ *			// You can use a class inheriting from `Delta` if that class should handle OT in a special way.
  *			const delta = new Delta();
  *
- *			// Add delta to the Batch instance. It is important to add delta to batch before applying any operation.
+ *			// Add delta to the Batch instance. It is important to add a delta to the batch before applying any operation.
  *			this.addDelta( delta );
  *
  *			// Create operations which should be components of this delta.

+ 33 - 27
packages/ckeditor5-engine/src/model/delta/attributedelta.js

@@ -16,7 +16,7 @@ import RootElement from '../rootelement.js';
 import Element from '../element.js';
 
 /**
- * To provide specific OT behavior and better collisions solving, change methods ({@link engine.model.Batch#setAttr}
+ * To provide specific OT behavior and better collisions solving, methods to change attributes ({@link engine.model.Batch#setAttr}
  * and {@link engine.model.Batch#removeAttr}) use `AttributeDelta` class which inherits from the `Delta` class and may
  * overwrite some methods.
  *
@@ -27,6 +27,7 @@ export default class AttributeDelta extends Delta {
 	/**
 	 * The attribute key that is changed by the delta or `null` if the delta has no operations.
 	 *
+	 * @readonly
 	 * @type {String|null}
 	 */
 	get key() {
@@ -36,6 +37,7 @@ export default class AttributeDelta extends Delta {
 	/**
 	 * The attribute value that is set by the delta or `null` if the delta has no operations.
 	 *
+	 * @readonly
 	 * @type {*|null}
 	 */
 	get value() {
@@ -45,6 +47,7 @@ export default class AttributeDelta extends Delta {
 	/**
 	 * The range on which delta operates or `null` if the delta has no operations.
 	 *
+	 * @readonly
 	 * @type {engine.model.Range|null}
 	 */
 	get range() {
@@ -77,13 +80,16 @@ export default class AttributeDelta extends Delta {
 		return 'engine.model.delta.AttributeDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 20;
 	}
 }
 
 /**
- * To provide specific OT behavior and better collisions solving, change methods ({@link engine.model.Batch#setAttr}
+ * To provide specific OT behavior and better collisions solving, methods to change attributes ({@link engine.model.Batch#setAttr}
  * and {@link engine.model.Batch#removeAttr}) use `RootAttributeDelta` class which inherits from the `Delta` class and may
  * overwrite some methods.
  *
@@ -100,62 +106,63 @@ export class RootAttributeDelta extends Delta {
 }
 
 /**
- * Sets the value of the attribute of the node or on the range.
+ * Sets value of the attribute with given key on a {@link engine.model.Item model item} or on a {@link engine.model.Range range}.
  *
  * @chainable
  * @method engine.model.Batch#setAttr
  * @param {String} key Attribute key.
  * @param {*} value Attribute new value.
- * @param {engine.model.Node|engine.model.Range} nodeOrRange Node or range on which the attribute will be set.
+ * @param {engine.model.Item|engine.model.Range} itemOrRange Model item or range on which the attribute will be set.
  */
-register( 'setAttr', function( key, value, nodeOrRange ) {
-	attribute( this, key, value, nodeOrRange );
+register( 'setAttr', function( key, value, itemOrRange ) {
+	attribute( this, key, value, itemOrRange );
 
 	return this;
 } );
 
 /**
- * Removes an attribute from the range.
+ * Removes an attribute with given key from a {@link engine model.Item model item} or from a {@link engine.model.Range range}.
  *
  * @chainable
  * @method engine.model.Batch#removeAttr
  * @param {String} key Attribute key.
- * @param {engine.model.Node|engine.model.Range} nodeOrRange Node or range on which the attribute will be removed.
+ * @param {engine.model.Item|engine.model.Range} itemOrRange Model item or range from which the attribute will be removed.
  */
-register( 'removeAttr', function( key, nodeOrRange ) {
-	attribute( this, key, null, nodeOrRange );
+register( 'removeAttr', function( key, itemOrRange ) {
+	attribute( this, key, null, itemOrRange );
 
 	return this;
 } );
 
-function attribute( batch, key, value, nodeOrRange ) {
-	if ( nodeOrRange instanceof Range ) {
-		changeRange( batch, batch.document, key, value, nodeOrRange );
+function attribute( batch, key, value, itemOrRange ) {
+	if ( itemOrRange instanceof Range ) {
+		changeRange( batch, batch.document, key, value, itemOrRange );
 	} else {
-		changeNode( batch, batch.document, key, value, nodeOrRange );
+		changeItem( batch, batch.document, key, value, itemOrRange );
 	}
 }
 
-function changeNode( batch, doc, key, value, node ) {
-	const previousValue = node.getAttribute( key );
+function changeItem( batch, doc, key, value, item ) {
+	const previousValue = item.getAttribute( key );
 	let range, operation;
 
-	const delta = node instanceof RootElement ? new RootAttributeDelta() : new AttributeDelta();
+	const delta = item instanceof RootElement ? new RootAttributeDelta() : new AttributeDelta();
 	batch.addDelta( delta );
 
 	if ( previousValue != value ) {
-		if ( node instanceof RootElement ) {
+		if ( item instanceof RootElement ) {
 			// If we change attributes of root element, we have to use `RootAttributeOperation`.
-			operation = new RootAttributeOperation( node, key, previousValue, value, doc.version );
+			operation = new RootAttributeOperation( item, key, previousValue, value, doc.version );
 		} else {
-			if ( node instanceof Element ) {
+			if ( item instanceof Element ) {
 				// If we change the attribute of the element, we do not want to change attributes of its children, so
-				// the end on the range can not be put after the closing tag, it should be inside that element with the
-				// offset 0, so the range will contains only the opening tag...
-				range = new Range( Position.createBefore( node ), Position.createFromParentAndOffset( node, 0 ) );
+				// the end of the range cannot be after the closing tag, it should be inside that element, before any of
+				// it's children, so the range will contain only the opening tag.
+				range = new Range( Position.createBefore( item ), Position.createFromParentAndOffset( item, 0 ) );
 			} else {
-				// ...but for characters we can not put the range inside it, so we end the range after that character.
-				range = new Range( Position.createBefore( node ), Position.createAfter( node ) );
+				// If `item` is text proxy, we create a range from the beginning to the end of that text proxy, to change
+				// all characters represented by it.
+				range = new Range( Position.createBefore( item ), Position.createAfter( item ) );
 			}
 
 			operation = new AttributeOperation( range, key, previousValue || null, value || null, doc.version );
@@ -166,7 +173,7 @@ function changeNode( batch, doc, key, value, node ) {
 	}
 }
 
-// Because attribute operation needs to have the same attribute value on the whole range, this function split the range
+// Because attribute operation needs to have the same attribute value on the whole range, this function splits the range
 // into smaller parts.
 function changeRange( batch, doc, attributeKey, attributeValue, range ) {
 	const delta = new AttributeDelta();
@@ -217,5 +224,4 @@ function changeRange( batch, doc, attributeKey, attributeValue, range ) {
 }
 
 DeltaFactory.register( AttributeDelta );
-
 DeltaFactory.register( RootAttributeDelta );

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

@@ -22,6 +22,7 @@ export default class InsertDelta extends Delta {
 	/**
 	 * Position where the delta inserts nodes or `null` if there are no operations in the delta.
 	 *
+	 * @readonly
 	 * @type {engine.model.Position|null}
 	 */
 	get position() {
@@ -31,6 +32,7 @@ export default class InsertDelta extends Delta {
 	/**
 	 * Node list containing all the nodes inserted by the delta or `null` if there are no operations in the delta.
 	 *
+	 * @readonly
 	 * @type {engine.model.NodeList|null}
 	 */
 	get nodes() {
@@ -40,6 +42,7 @@ export default class InsertDelta extends Delta {
 	/**
 	 * Insert operation that is saved in this delta or `null` if there are no operations in the delta.
 	 *
+	 * @readonly
 	 * @protected
 	 * @type {engine.model.operation.InsertOperation|null}
 	 */
@@ -47,6 +50,9 @@ export default class InsertDelta extends Delta {
 		return this.operations[ 0 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return RemoveDelta;
 	}
@@ -58,6 +64,9 @@ export default class InsertDelta extends Delta {
 		return 'engine.model.delta.InsertDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 20;
 	}

+ 10 - 4
packages/ckeditor5-engine/src/model/delta/mergedelta.js

@@ -26,12 +26,16 @@ export default class MergeDelta extends Delta {
 	/**
 	 * Position between to merged nodes or `null` if the delta has no operations.
 	 *
+	 * @readonly
 	 * @type {engine.model.Position|null}
 	 */
 	get position() {
 		return this._removeOperation ? this._removeOperation.sourcePosition : null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	getReversed() {
 		let delta = super.getReversed();
 
@@ -48,6 +52,7 @@ export default class MergeDelta extends Delta {
 	 * this might be an instance of {@link engine.model.operation.MoveOperation} instead of
 	 * {@link engine.model.operation.RemoveOperation}.
 	 *
+	 * @readonly
 	 * @protected
 	 * @type {engine.model.operation.MoveOperation|null}
 	 */
@@ -55,6 +60,9 @@ export default class MergeDelta extends Delta {
 		return this.operations[ 1 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return SplitDelta;
 	}
@@ -90,8 +98,7 @@ register( 'merge', function( position ) {
 		 *
 		 * @error batch-merge-no-element-before
 		 */
-		throw new CKEditorError(
-			'batch-merge-no-element-before: Node before merge position must be an element.' );
+		throw new CKEditorError( 'batch-merge-no-element-before: Node before merge position must be an element.' );
 	}
 
 	if ( !( nodeAfter instanceof Element ) ) {
@@ -100,8 +107,7 @@ register( 'merge', function( position ) {
 		 *
 		 * @error batch-merge-no-element-after
 		 */
-		throw new CKEditorError(
-			'batch-merge-no-element-after: Node after merge position must be an element.' );
+		throw new CKEditorError( 'batch-merge-no-element-after: Node after merge position must be an element.' );
 	}
 
 	const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );

+ 16 - 9
packages/ckeditor5-engine/src/model/delta/movedelta.js

@@ -22,7 +22,7 @@ import CKEditorError from '../../../utils/ckeditorerror.js';
  */
 export default class MoveDelta extends Delta {
 	/**
-	 * How many nodes are moved by the delta or `null` if there are no operations in the delta.
+	 * Offset size of moved range or `null` if there are no operations in the delta.
 	 *
 	 * @type {Number|null}
 	 */
@@ -53,7 +53,8 @@ export default class MoveDelta extends Delta {
 	}
 
 	/**
-	 * Move operation that is saved in this delta or `null` if there are no operations in the delta.
+	 * {@link engine.model.delta.MoveDelta#_moveOperation Move operation} that is saved in this delta or `null`
+	 * if there are no operations in the delta.
 	 *
 	 * @protected
 	 * @type {engine.model.operation.MoveOperation|null}
@@ -62,6 +63,9 @@ export default class MoveDelta extends Delta {
 		return this.operations[ 0 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return MoveDelta;
 	}
@@ -73,6 +77,9 @@ export default class MoveDelta extends Delta {
 		return 'engine.model.delta.MoveDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 20;
 	}
@@ -85,19 +92,19 @@ function addMoveOperation( batch, delta, sourcePosition, howMany, targetPosition
 }
 
 /**
- * Moves given node or given range of nodes to target position.
+ * Moves given {@link engine.model.Item model item} or given range to target position.
  *
  * @chainable
  * @method engine.model.Batch#move
- * @param {engine.model.Node|engine.model.Range} nodeOrRange Node or range of nodes to move.
+ * @param {engine.model.Item|engine.model.Range} itemOrRange Model item or range of nodes to move.
  * @param {engine.model.Position} targetPosition Position where moved nodes will be inserted.
  */
-register( 'move', function( nodeOrRange, targetPosition ) {
+register( 'move', function( itemOrRange, targetPosition ) {
 	const delta = new MoveDelta();
 	this.addDelta( delta );
 
-	if ( nodeOrRange instanceof Range ) {
-		if ( !nodeOrRange.isFlat ) {
+	if ( itemOrRange instanceof Range ) {
+		if ( !itemOrRange.isFlat ) {
 			/**
 			 * Range to move is not flat.
 			 *
@@ -106,9 +113,9 @@ register( 'move', function( nodeOrRange, targetPosition ) {
 			throw new CKEditorError( 'batch-move-range-not-flat: Range to move is not flat.' );
 		}
 
-		addMoveOperation( this, delta, nodeOrRange.start, nodeOrRange.end.offset - nodeOrRange.start.offset, targetPosition );
+		addMoveOperation( this, delta, itemOrRange.start, itemOrRange.end.offset - itemOrRange.start.offset, targetPosition );
 	} else {
-		addMoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1, targetPosition );
+		addMoveOperation( this, delta, Position.createBefore( itemOrRange ), 1, targetPosition );
 	}
 
 	return this;

+ 6 - 6
packages/ckeditor5-engine/src/model/delta/removedelta.js

@@ -35,25 +35,25 @@ function addRemoveOperation( batch, delta, position, howMany ) {
 }
 
 /**
- * Removes given node or range of nodes.
+ * Removes given {@link engine.model.Item model item} or given range.
  *
  * @chainable
  * @method engine.model.Batch#remove
- * @param {engine.model.Node|engine.model.Range} nodeOrRange Node or range of nodes to remove.
+ * @param {engine.model.Item|engine.model.Range} itemOrRange Model item or range to remove.
  */
-register( 'remove', function( nodeOrRange ) {
+register( 'remove', function( itemOrRange ) {
 	const delta = new RemoveDelta();
 	this.addDelta( delta );
 
-	if ( nodeOrRange instanceof Range ) {
+	if ( itemOrRange instanceof Range ) {
 		// The array is reversed, so the ranges are correct and do not have to be updated.
-		let ranges = nodeOrRange.getMinimalFlatRanges().reverse();
+		let ranges = itemOrRange.getMinimalFlatRanges().reverse();
 
 		for ( let flat of ranges ) {
 			addRemoveOperation( this, delta, flat.start, flat.end.offset - flat.start.offset );
 		}
 	} else {
-		addRemoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1 );
+		addRemoveOperation( this, delta, Position.createBefore( itemOrRange ), 1 );
 	}
 
 	return this;

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

@@ -21,6 +21,9 @@ import Position from '../position.js';
  * @memberOf engine.model.delta
  */
 export default class RenameDelta extends Delta {
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return RenameDelta;
 	}
@@ -39,7 +42,7 @@ function apply( batch, delta, operation ) {
 }
 
 /**
- * Renames the given element.
+ * Renames given element.
  *
  * @chainable
  * @method engine.model.Batch#rename

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

@@ -32,6 +32,9 @@ export default class SplitDelta extends Delta {
 		return this._moveOperation ? this._moveOperation.sourcePosition : null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	getReversed() {
 		let delta = super.getReversed();
 
@@ -43,12 +46,13 @@ export default class SplitDelta extends Delta {
 	}
 
 	/**
-	 * Operation in the delta that adds a node to the tree model where split elements will be moved to or `null` if
+	 * Operation in the delta that adds to model an element into which split nodes will be moved, or `null` if
 	 * there are no operations in the delta.
 	 *
-	 * Most commonly this will be insert operation, as `SplitDelta` has to create a new node. If `SplitDelta` was created
-	 * through {@link engine.model.delta.MergeDelta MergeDelta} {@link engine.model.delta.Delta#getReversed reversing},
-	 * this will be a reinsert operation, as we will want to "insert-back" the node that was removed by `MergeDelta`.
+	 * Most commonly this will be {@link engine.model.operation.InsertOperation an insert operation}, as `SplitDelta`
+	 * has to create a new node. If `SplitDelta` was created through {@link engine.model.delta.Delta#getReversed reversing}
+	 * a {@link engine.model.delta.MergeDelta merge delta}, this will be a {@link engine.model.operation.ReinsertOperation reinsert operation},
+	 * as we will want to re-insert the exact element that was removed by that merge delta.
 	 *
 	 * @protected
 	 * @type {engine.model.operation.InsertOpertaion|engine.model.operation.ReinsertOperation|null}
@@ -58,8 +62,8 @@ export default class SplitDelta extends Delta {
 	}
 
 	/**
-	 * Operation in the delta that moves nodes from after split position to their new parent
-	 * or `null` if there are no operations in the delta.
+	 * Operation in the delta that moves model items, that are after split position, to their new parent or `null`
+	 * if there are no operations in the delta.
 	 *
 	 * @protected
 	 * @type {engine.model.operation.MoveOperation|null}
@@ -68,6 +72,9 @@ export default class SplitDelta extends Delta {
 		return this.operations[ 1 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return MergeDelta;
 	}
@@ -79,15 +86,18 @@ export default class SplitDelta extends Delta {
 		return 'engine.model.delta.SplitDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 5;
 	}
 }
 
 /**
- * Splits a node at the given position.
+ * Splits an element at the given position.
  *
- * This cannot be a position inside the root element. The `batch-split-root` error will be thrown if
+ * The element cannot be a root element, as root element cannot be split. The `batch-split-root` error will be thrown if
  * you try to split the root element.
  *
  * @chainable

+ 1 - 1
packages/ckeditor5-engine/src/model/delta/transform.js

@@ -62,7 +62,7 @@ function updateBaseVersion( baseVersion, deltas ) {
 /**
  * The default delta transformation function. It is used for those deltas that are not in special case conflict.
  *
- * This algorithm is similar to popular `dOPT` algorithm used in operational transformation, as we are in fact
+ * This algorithm is similar to a popular `dOPT` algorithm used in operational transformation, as we are in fact
  * transforming two sets of operations by each other.
  *
  * @external engine.model.delta.transform

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

@@ -41,6 +41,9 @@ export default class UnwrapDelta extends Delta {
 		return this.operations[ 0 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return WrapDelta;
 	}
@@ -52,13 +55,16 @@ export default class UnwrapDelta extends Delta {
 		return 'engine.model.delta.UnwrapDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 10;
 	}
 }
 
 /**
- * Unwraps specified element, that is moves all it's children before it and then removes it. Throws
+ * Unwraps given element, that is moves all it's children before it and then removes it. Throws
  * error if you try to unwrap an element that does not have a parent.
  *
  * @chainable

+ 6 - 6
packages/ckeditor5-engine/src/model/delta/weakinsertdelta.js

@@ -28,18 +28,18 @@ export default class WeakInsertDelta extends InsertDelta {
 }
 
 /**
- * Inserts a node or nodes at the given position. {@link engine.model.Batch#weakInsert weakInsert} is commonly used for actions
+ * Inserts a node or nodes at given position. {@link engine.model.Batch#weakInsert weakInsert} is commonly used for actions
  * like typing or plain-text paste (without formatting). There are two differences between
  * {@link engine.model.Batch#insert insert} and {@link engine.model.Batch#weakInsert weakInsert}:
  *
  * * When using `weakInsert`, inserted nodes will have same attributes as the current attributes of
  * {@link engine.model.Document#selection document selection}.
- * * Normal behavior is that inserting inside range changed by
- * {@link engine.model.operation.AttributeOperation AttributeOperation} splits
- * the operation into two operations, which "omit" the inserted nodes. The correct behavior for `WeakInsertDelta` is that
+ * * If {@link engine.model.operation.InsertOperation insert operation} position is inside a range changed by
+ * {@link engine.model.operation.AttributeOperation attribute operation}, the attribute operation is split into two operations.
+ * Thanks to this, attribute change "omits" the inserted nodes. The correct behavior for `WeakInsertDelta` is that
  * {@link engine.model.operation.AttributeOperation AttributeOperation} does not "break" and also
- * applies attributes for inserted nodes.
- * The above has to be reflected during {@link engine.model.operation.transform operational transformation}.
+ * applies attributes for inserted nodes. This behavior has to be reflected during
+ * {@link engine.model.delta.transform delta transformation}.
  *
  * @chainable
  * @method engine.model.Batch#weakInsert

+ 12 - 3
packages/ckeditor5-engine/src/model/delta/wrapdelta.js

@@ -36,7 +36,7 @@ export default class WrapDelta extends Delta {
 	}
 
 	/**
-	 * How many nodes is wrapped by the delta or `null` if there are no operations in delta.
+	 * Offset size of range to wrap by the delta or `null` if there are no operations in delta.
 	 *
 	 * @type {Number}
 	 */
@@ -66,22 +66,31 @@ export default class WrapDelta extends Delta {
 		return this.operations[ 1 ] || null;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get _reverseDeltaClass() {
 		return UnwrapDelta;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.delta.WrapDelta';
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	static get _priority() {
 		return 10;
 	}
 }
 
 /**
- * Wraps given range with given element or with a new element of specified name if string has been passed.
- * **Note:** given range should be a "flat range" (see {@link engine.model.Range#isFlat}). If not, error will be thrown.
+ * Wraps given range with given element or with a new element with specified name, if string has been passed.
+ * **Note:** range to wrap should be a "flat range" (see {@link engine.model.Range#isFlat}). If not, error will be thrown.
  *
  * @chainable
  * @method engine.model.Batch#wrap

+ 6 - 5
packages/ckeditor5-engine/src/model/document.js

@@ -13,12 +13,12 @@ import RootElement from './rootelement.js';
 import Batch from './batch.js';
 import History from './history.js';
 import LiveSelection from './liveselection.js';
-import EmitterMixin from '../../utils/emittermixin.js';
-import CKEditorError from '../../utils/ckeditorerror.js';
-import mix from '../../utils/mix.js';
 import Schema from './schema.js';
 import Composer from './composer/composer.js';
 import clone from '../../utils/lib/lodash/clone.js';
+import EmitterMixin from '../../utils/emittermixin.js';
+import CKEditorError from '../../utils/ckeditorerror.js';
+import mix from '../../utils/mix.js';
 
 const graveyardName = '$graveyard';
 
@@ -28,7 +28,7 @@ const graveyardName = '$graveyard';
  * represented by the separate root.
  *
  * All changes in the document are done by {@link engine.model.operation.Operation operations}. To create operations in
- * the simple way use use the {@link engine.model.Batch} API, for example:
+ * a simple way, use the {@link engine.model.Batch} API, for example:
  *
  *		doc.batch().insert( position, nodes ).split( otherPosition );
  *
@@ -38,7 +38,8 @@ const graveyardName = '$graveyard';
  */
 export default class Document {
 	/**
-	 * Creates an empty document instance with no {@link engine.model.Document#roots} (other than graveyard).
+	 * Creates an empty document instance with no {@link engine.model.Document#roots} (other than
+	 * a {@link engine.model.Document#graveyard graveyard root}).
 	 */
 	constructor() {
 		/**

+ 3 - 3
packages/ckeditor5-engine/src/model/history.js

@@ -14,7 +14,7 @@ import CKEditorError from '../../utils/ckeditorerror.js';
  *
  * **Note:** deltas kept in `History` should be used only to transform deltas. It's not advised to use `History` to get
  * original delta basing on it's {@link engine.model.delta.Delta#baseVersion baseVersion}. Also, after transforming a
- * delta by deltas from `History`, fix it's base version accordingly (set to {@link engine.model.Document#version}.
+ * delta by deltas from `History`, fix it's base version accordingly (set it to {@link engine.model.Document#version document version}).
  *
  * @memberOf engine.model
  */
@@ -58,7 +58,7 @@ export default class History {
 	/**
 	 * Returns deltas added to the history.
 	 *
-	 * @param {Number} [from=0] Base version from which deltas should be returned (inclusive). Defaults to `0` which means
+	 * @param {Number} [from=0] Base version from which deltas should be returned (inclusive). Defaults to `0`, which means
 	 * that deltas from the first one will be returned.
 	 * @param {Number} [to=Number.POSITIVE_INFINITY] Base version up to which deltas should be returned (exclusive).
 	 * Defaults to `Number.POSITIVE_INFINITY` which means that deltas up to the last one will be returned.
@@ -145,7 +145,7 @@ export default class History {
 	 * **Note:** if delta with `baseVersion` was already updated by multiple deltas, all updated deltas will be removed
 	 * and new deltas will be inserted at their position.
 	 *
-	 * **Note:** delta marked as reversed won't get updated.
+	 * **Note:** removed delta won't get updated.
 	 *
 	 * @param {Number} baseVersion Base version of a delta to update.
 	 * @param {Iterable.<engine.model.delta.Delta>} updatedDeltas Deltas to be inserted in place of updated delta.

+ 14 - 11
packages/ckeditor5-engine/src/model/liveposition.js

@@ -13,12 +13,14 @@ import mix from '../../utils/mix.js';
 import CKEditorError from '../../utils/ckeditorerror.js';
 
 /**
- * LivePosition is a position in {@link engine.model.Document Document} that updates itself as Document is changed
- * through operations. It may be used as a bookmark in the Document.
- * **Note:** Contrary to {@link engine.model.Position}, LivePosition works only in roots that are
+ * `LivePosition` is a type of {@link engine.model.Position Position} that updates itself as {@link engine.model.Document document}
+ * is changed through operations. It may be used as a bookmark.
+ *
+ * **Note:** Contrary to {@link engine.model.Position}, `LivePosition` works only in roots that are
  * {@link engine.model.RootElement}. If {@link engine.model.DocumentFragment} is passed, error will be thrown.
- * **Note:** Be very careful when dealing with LivePosition. Each LivePosition instance bind events that might
- * have to be unbound. Use {@link engine.model.LivePosition#detach} whenever you don't need LivePosition anymore.
+ *
+ * **Note:** Be very careful when dealing with `LivePosition`. Each `LivePosition` instance bind events that might
+ * have to be unbound. Use {@link engine.model.LivePosition#detach} whenever you don't need `LivePosition` anymore.
  *
  * @memberOf engine.model
  * @extends engine.model.Position
@@ -46,10 +48,10 @@ export default class LivePosition extends Position {
 		super( root, path );
 
 		/**
-		 * Flag representing LivePosition stickiness. LivePosition might be sticking to previous node or next node.
-		 * Whenever some nodes are inserted at the same position as LivePosition, `stickiness` is checked to decide if
+		 * Flag representing `LivePosition` stickiness. `LivePosition` might be sticking to previous node or next node.
+		 * Whenever some nodes are inserted at the same position as `LivePosition`, `stickiness` is checked to decide if
 		 * LivePosition should be moved. Similar applies when a range of nodes is moved and one of it's boundary
-		 * position is same as LivePosition.
+		 * position is same as `LivePosition`.
 		 *
 		 * Examples:
 		 *
@@ -71,7 +73,7 @@ export default class LivePosition extends Position {
 	}
 
 	/**
-	 * Unbinds all events previously bound by LivePosition. Use it whenever you don't need LivePosition instance
+	 * Unbinds all events previously bound by `LivePosition`. Use it whenever you don't need `LivePosition` instance
 	 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
 	 * referring to it).
 	 */
@@ -114,7 +116,8 @@ export default class LivePosition extends Position {
 }
 
 /**
- * Binds this LivePosition to the {@link engine.model.Document} that owns this position {@link engine.model.RootElement root}.
+ * Binds this `LivePosition` to the {@link engine.model.Document document} that owns
+ * this position's {@link engine.model.Position#root root}.
  *
  * @ignore
  * @private
@@ -134,7 +137,7 @@ function bindWithDocument() {
 }
 
 /**
- * Updates this position accordingly to the updates applied to the Tree Model. Bases on change events.
+ * Updates this position accordingly to the updates applied to the model. Bases on change events.
  *
  * @ignore
  * @private

+ 8 - 9
packages/ckeditor5-engine/src/model/liverange.js

@@ -10,7 +10,8 @@ import EmitterMixin from '../../utils/emittermixin.js';
 import mix from '../../utils/mix.js';
 
 /**
- * LiveRange is a Range in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
+ * `LiveRange` is a type of {@link engine.model.Range Range} that updates itself as {@link engine.model.Document document}
+ * is changed through operations. It may be used as a bookmark.
  *
  * **Note:** Be very careful when dealing with `LiveRange`. Each `LiveRange` instance bind events that might
  * have to be unbound. Use {@link engine.model.LiveRange#detach detach} whenever you don't need `LiveRange` anymore.
@@ -30,7 +31,7 @@ export default class LiveRange extends Range {
 	}
 
 	/**
-	 * Unbinds all events previously bound by LiveRange. Use it whenever you don't need LiveRange instance
+	 * Unbinds all events previously bound by `LiveRange`. Use it whenever you don't need `LiveRange` instance
 	 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
 	 * referring to it).
 	 */
@@ -76,7 +77,7 @@ export default class LiveRange extends Range {
 }
 
 /**
- * Binds this LiveRange to the {@link engine.model.Document} that owns this range.
+ * Binds this `LiveRange` to the {@link engine.model.Document document} that owns this range's {@link engine.model.Range#root root}.
  *
  * @ignore
  * @private
@@ -89,25 +90,23 @@ function bindWithDocument() {
 		this.root.document,
 		'change',
 		( event, type, changes ) => {
-			fixBoundaries.call( this, type, changes.range, changes.sourcePosition );
+			transform.call( this, type, changes.range, changes.sourcePosition );
 		},
 		this
 	);
 }
 
 /**
- * LiveRange boundaries are instances of {@link engine.model.LivePosition}, so it is updated thanks to them. This method
- * additionally fixes the results of updating live positions taking into account that those live positions
- * are boundaries of a range. An example case for fixing live positions is end boundary is moved before start boundary.
+ * Updates this range accordingly to the updates applied to the model. Bases on change events.
  *
  * @ignore
  * @private
- * @method fixBoundaries
+ * @method transform
  * @param {String} type Type of changes applied to the Tree Model.
  * @param {engine.model.Range} range Range containing the result of applied change.
  * @param {engine.model.Position} [position] Additional position parameter provided by some change events.
  */
-function fixBoundaries( type, range, position ) {
+function transform( type, range, position ) {
 	/* jshint validthis: true */
 	let updated;
 	const howMany = range.end.offset - range.start.offset;

+ 11 - 8
packages/ckeditor5-engine/src/model/liveselection.js

@@ -17,21 +17,24 @@ import Selection from './selection.js';
 const storePrefix = 'selection:';
 
 /**
- * `LiveSelection` is a special type of {@link engine.model.Selection selection} that listens to changes on a
+ * `LiveSelection` is a type of {@link engine.model.Selection selection} that listens to changes on a
  * {@link engine.model.Document document} and has it ranges updated accordingly. Internal implementation of this
  * mechanism bases on {@link engine.model.LiveRange live ranges}.
  *
- * Differences between {@link engine.model.Selection} and `LiveSelection` are three:
- * * there is always a range in `LiveSelection`, even if no ranges were added - in this case, there is a
- * "default range" in selection which is a collapsed range set at the beginning of the {@link engine.model.Document document},
- * * ranges added to this selection updates automatically when the document changes,
- * * live selection may have attributes.
+ * Differences between {@link engine.model.Selection} and `LiveSelection` are two:
+ * * there is always a range in `LiveSelection` - even if no ranges were added there is a
+ * {@link engine.model.LiveSelection#_getDefaultRange "default range"} present in the selection,
+ * * ranges added to this selection updates automatically when the document changes.
+ *
+ * Since `LiveSelection` uses {@link engine.model.LiveRange live ranges} and is updated when {@link engine.model.Document document}
+ * changes, it cannot be set on {@link engine.model.Node nodes} that are inside {@link engine.model.DocumentFragment document fragment}.
+ * If you need to represent a selection in document fragment, use {@link engine.model.Selection "normal" selection} instead.
  *
  * @memberOf engine.model
  */
 export default class LiveSelection extends Selection {
 	/**
-	 * Creates an empty document selection for given {@link engine.model.Document}.
+	 * Creates an empty live selection for given {@link engine.model.Document}.
 	 *
 	 * @param {engine.model.Document} document Document which owns this selection.
 	 */
@@ -169,7 +172,7 @@ export default class LiveSelection extends Selection {
 
 	/**
 	 * Returns a default range for this selection. The default range is a collapsed range that starts and ends
-	 * at the beginning of this selection's document {@link engine.model.Document#_getDefaultRoot default root}.
+	 * at the beginning of this selection's document's {@link engine.model.Document#_getDefaultRoot default root}.
 	 * This "artificial" range is important for algorithms that base on selection, so they won't break or need
 	 * special logic if there are no real ranges in the selection.
 	 *

+ 22 - 12
packages/ckeditor5-engine/src/model/operation/attributeoperation.js

@@ -11,7 +11,9 @@ import CKEditorError from '../../../utils/ckeditorerror.js';
 import writer from '../writer.js';
 
 /**
- * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
+ * Operation to change nodes' attribute.
+ *
+ * Using this class you can add, remove or change value of the attribute.
  *
  * @memberOf engine.model.operation
  * @extends engine.model.operation.Operation
@@ -20,19 +22,19 @@ export default class AttributeOperation extends Operation {
 	/**
 	 * Creates an operation that changes, removes or adds attributes.
 	 *
-	 * If only the new attribute is set, then it will be inserted. Note that in all nodes in ranges there must be
-	 * no attributes with the same key as the new attribute.
+	 * If only `newValue` is set, attribute will be added on a node. Note that all nodes in operation's range must not
+	 * have an attribute with the same key as the added attribute.
 	 *
-	 * If only the old attribute is set, then it will be removed. Note that this attribute must be present in all nodes in
-	 * ranges.
+	 * If only `oldValue` is set, then attribute with given key will be removed. Note that all nodes in operation's range
+	 * must have an attribute with that key added.
 	 *
-	 * If both new and old attributes are set, then the operation will change the attribute value. Note that both new and
-	 * old attributes have to have the same key and the old attribute must be present in all nodes in ranges.
+	 * If both `newValue` and `oldValue` are set, then the operation will change the attribute value. Note that all nodes in
+	 * operation's ranges must already have an attribute with given key and `oldValue` as value
 	 *
 	 * @param {engine.model.Range} range Range on which the operation should be applied.
 	 * @param {String} key Key of an attribute to change or remove.
-	 * @param {*} oldValue Old value of the attribute with given key or `null` if adding a new attribute.
-	 * @param {*} newValue New value to set for the attribute. If `null`, then the operation just removes the attribute.
+	 * @param {*} oldValue Old value of the attribute with given key or `null`, if attribute was not set before.
+	 * @param {*} newValue New value of the attribute with given key or `null`, if operation should remove attribute.
 	 * @param {Number} baseVersion {@link engine.model.Document#version} on which the operation can be applied.
 	 */
 	constructor( range, key, oldValue, newValue, baseVersion ) {
@@ -55,7 +57,7 @@ export default class AttributeOperation extends Operation {
 		this.key = key;
 
 		/**
-		 * Old value of the attribute with given key or `null` if adding a new attribute.
+		 * Old value of the attribute with given key or `null`, if attribute was not set before.
 		 *
 		 * @readonly
 		 * @member {*} engine.model.operation.AttributeOperation#oldValue
@@ -63,7 +65,7 @@ export default class AttributeOperation extends Operation {
 		this.oldValue = oldValue;
 
 		/**
-		 * New value to set for the attribute. If `null`, then the operation just removes the attribute.
+		 * New value of the attribute with given key or `null`, if operation should remove attribute.
 		 *
 		 * @readonly
 		 * @member {*} engine.model.operation.AttributeOperation#newValue
@@ -71,6 +73,9 @@ export default class AttributeOperation extends Operation {
 		this.newValue = newValue;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get type() {
 		if ( this.oldValue === null ) {
 			return 'addAttribute';
@@ -82,6 +87,7 @@ export default class AttributeOperation extends Operation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.AttributeOperation}
 	 */
 	clone() {
@@ -89,12 +95,16 @@ export default class AttributeOperation extends Operation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.AttributeOperation}
 	 */
 	getReversed() {
 		return new AttributeOperation( this.range, this.key, this.newValue, this.oldValue, this.baseVersion + 1 );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	_execute() {
 		// Validation.
 		for ( let item of this.range.getItems() ) {
@@ -141,7 +151,7 @@ export default class AttributeOperation extends Operation {
 	}
 
 	/**
-	 * Creates AttributeOperation object from deserilized object, i.e. from parsed JSON string.
+	 * Creates `AttributeOperation` object from deserilized object, i.e. from parsed JSON string.
 	 *
 	 * @param {Object} json Deserialized JSON object.
 	 * @param {engine.model.Document} document Document on which this operation will be applied.

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

@@ -15,7 +15,7 @@ import Text from '../text.js';
 import Element from '../element.js';
 
 /**
- * Operation to insert list of nodes on the given position in the tree data model.
+ * Operation to insert one or more nodes at given position in the model.
  *
  * @memberOf engine.model.operation
  * @extends engine.model.operation.Operation
@@ -48,11 +48,15 @@ export default class InsertOperation extends Operation {
 		this.nodes = new NodeList( normalizeNodes( nodes ) );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get type() {
 		return 'insert';
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.InsertOperation}
 	 */
 	clone() {
@@ -60,12 +64,16 @@ export default class InsertOperation extends Operation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.RemoveOperation}
 	 */
 	getReversed() {
 		return new RemoveOperation( this.position, this.nodes.totalOffset, this.baseVersion + 1 );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	_execute() {
 		// What happens here is that we want original nodes be passed to writer because we want original nodes
 		// to be inserted to the model. But in InsertOperation, we want to keep those nodes as they were added
@@ -87,7 +95,7 @@ export default class InsertOperation extends Operation {
 	}
 
 	/**
-	 * Creates InsertOperation object from deserilized object, i.e. from parsed JSON string.
+	 * Creates `InsertOperation` object from deserilized object, i.e. from parsed JSON string.
 	 *
 	 * @param {Object} json Deserialized JSON object.
 	 * @param {engine.model.Document} document Document on which this operation will be applied.

+ 17 - 9
packages/ckeditor5-engine/src/model/operation/moveoperation.js

@@ -13,39 +13,39 @@ import compareArrays from '../../../utils/comparearrays.js';
 import writer from './../writer.js';
 
 /**
- * Operation to move list of subsequent nodes from one position in the document to another.
+ * Operation to move a range of {@link engine.model.Item model items} to given {@link engine.model.Position target position}.
  *
  * @memberOf engine.model.operation
- * @extends engine.model.operation.Operation
  */
 export default class MoveOperation extends Operation {
 	/**
 	 * Creates a move operation.
 	 *
-	 * @param {engine.model.Position} sourcePosition Position before the first node to move.
-	 * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
-	 * @param {engine.model.Position} targetPosition Position where moved nodes will be inserted.
+	 * @param {engine.model.Position} sourcePosition Position before the first {@link engine.model.Item model item} to move.
+	 * @param {Number} howMany Offset size of moved range. Moved range will start from `sourcePosition` and end at
+	 * `sourcePosition` with offset shifted by `howMany`.
+	 * @param {engine.model.Position} targetPosition Position at which moved nodes will be inserted.
 	 * @param {Number} baseVersion {@link engine.model.Document#version} on which operation can be applied.
 	 */
 	constructor( sourcePosition, howMany, targetPosition, baseVersion ) {
 		super( baseVersion );
 
 		/**
-		 * Source move position.
+		 * Position before the first {@link engine.model.Item model item} to move.
 		 *
 		 * @member {engine.model.Position} engine.model.operation.MoveOperation#sourcePosition
 		 */
 		this.sourcePosition = Position.createFromPosition( sourcePosition );
 
 		/**
-		 * How many nodes to move.
+		 * Offset size of moved range.
 		 *
 		 * @member {Number} engine.model.operation.MoveOperation#howMany
 		 */
 		this.howMany = howMany;
 
 		/**
-		 * Target move position.
+		 * Position at which moved nodes will be inserted.
 		 *
 		 * @member {engine.model.Position} engine.model.operation.MoveOperation#targetPosition
 		 */
@@ -78,11 +78,15 @@ export default class MoveOperation extends Operation {
 		this.isSticky = false;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get type() {
 		return 'move';
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.MoveOperation}
 	 */
 	clone() {
@@ -93,6 +97,7 @@ export default class MoveOperation extends Operation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.MoveOperation}
 	 */
 	getReversed() {
@@ -104,6 +109,9 @@ export default class MoveOperation extends Operation {
 		return op;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	_execute() {
 		let sourceElement = this.sourcePosition.parent;
 		let targetElement = this.targetPosition.parent;
@@ -173,7 +181,7 @@ export default class MoveOperation extends Operation {
 	}
 
 	/**
-	 * Creates MoveOperation object from deserilized object, i.e. from parsed JSON string.
+	 * Creates `MoveOperation` object from deserilized object, i.e. from parsed JSON string.
 	 *
 	 * @param {Object} json Deserialized JSON object.
 	 * @param {engine.model.Document} document Document on which this operation will be applied.

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

@@ -8,9 +8,8 @@
 import Operation from './operation.js';
 
 /**
- * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop").
- * This is an operation, which when executed does not change the tree model.
- * It still has some parameters defined for transformation purposes.
+ * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop"). This is an operation,
+ * which when executed does not change the tree model. It still has some parameters defined for transformation purposes.
  *
  * In most cases this operation is a result of transforming operations. When transformation returns
  * {@link engine.model.operation.NoOperation} it means that changes done by the transformed operation
@@ -21,6 +20,7 @@ import Operation from './operation.js';
  */
 export default class NoOperation extends Operation {
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.NoOperation}
 	 */
 	clone() {
@@ -28,12 +28,16 @@ export default class NoOperation extends Operation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.NoOperation}
 	 */
 	getReversed() {
 		return new NoOperation( this.baseVersion + 1 );
 	}
 
+	/**
+	 * @inheritdoc
+	 */
 	_execute() {
 		// Do nothing.
 	}

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

@@ -32,7 +32,7 @@ operations[ RootAttributeOperation.className ] = RootAttributeOperation;
  */
 export default class OperationFactory {
 	/**
-	 * Creates concrete Operation object from deserilized object, i.e. from parsed JSON string.
+	 * Creates concrete `Operation` object from deserilized object, i.e. from parsed JSON string.
 	 *
 	 * @param {Object} json Deserialized JSON object.
 	 * @param {engine.model.Document} document Document on which this operation will be applied.

+ 11 - 9
packages/ckeditor5-engine/src/model/operation/reinsertoperation.js

@@ -9,20 +9,15 @@ import MoveOperation from './moveoperation.js';
 import RemoveOperation from './removeoperation.js';
 
 /**
- * Operation to reinsert previously removed nodes back to the non-graveyard root.
- * This is basically {@link engine.model.operation.MoveOperation} but it returns
- * {@link engine.model.operation.RemoveOperation} when reversed.
- *
- * With this class, we achieve two goals: by having separate classes it's easier to distinguish whether move
- * operation is actually a remove/reinsert operation and fire proper events. Also it
- * will be easier to expand if we need to change operation's behavior if it is remove/reinsert.
+ * Operation to reinsert previously removed nodes back to the non-graveyard root. This operation acts like
+ * {@link engine.model.operation.MoveOperation} but it returns {@link engine.model.operation.RemoveOperation} when reversed
+ * and fires different change event.
  *
  * @memberOf engine.model.operation
- * @extends engine.model.operation.Operation
  */
 export default class ReinsertOperation extends MoveOperation {
 	/**
-	 * Position where re-inserted node will be inserted.
+	 * Position where nodes will be re-inserted.
 	 *
 	 * @type {engine.model.Position}
 	 */
@@ -30,15 +25,22 @@ export default class ReinsertOperation extends MoveOperation {
 		return this.targetPosition;
 	}
 
+	/**
+	 * @param {engine.model.Position} pos
+	 */
 	set position( pos ) {
 		this.targetPosition = pos;
 	}
 
+	/**
+	 * @inheritDoc
+	 */
 	get type() {
 		return 'reinsert';
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.RemoveOperation}
 	 */
 	getReversed() {

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

@@ -14,15 +14,14 @@ import ReinsertOperation from './reinsertoperation.js';
  * Operation to remove a range of nodes.
  *
  * @memberOf engine.model.operation
- * @extends engine.model.operation.Operation
  */
 export default class RemoveOperation extends MoveOperation {
 	/**
-	 *
 	 * Creates a remove operation.
 	 *
-	 * @param {engine.model.Position} position Position before the first node to remove.
-	 * @param {Number} howMany How many nodes to remove.
+	 * @param {engine.model.Position} position Position before the first {@link engine.model.Item model item} to remove.
+	 * @param {Number} howMany Offset size of removed range. {@link engine.model.Item Model items} will be removed starting
+	 * from `sourcePosition`, up to a `sourcePosition` with offset shifted by `howMany`.
 	 * @param {Number} baseVersion {@link engine.model.Document#version} on which operation can be applied.
 	 */
 	constructor( position, howMany, baseVersion ) {
@@ -60,9 +59,11 @@ export default class RemoveOperation extends MoveOperation {
 	}
 
 	/**
-	 * Flag informing whether this operation should insert "holder" element (`true`) or should remove nodes
-	 * into existing "holder" element (`false`). It is `true` for each `RemoveOperation` that is the first `RemoveOperation`
-	 * in it's delta which points to given holder element.
+	 * Flag informing whether this operation should insert "holder" element (`true`) or should move removed nodes
+	 * into existing "holder" element (`false`).
+	 *
+	 * It is `true` for each `RemoveOperation` that is the first `RemoveOperation` in it's delta that points to given holder element.
+	 * This way only one `RemoveOperation` in given delta will insert "holder" element.
 	 *
 	 * @protected
 	 * @type {Boolean}
@@ -91,6 +92,7 @@ export default class RemoveOperation extends MoveOperation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.ReinsertOperation}
 	 */
 	getReversed() {
@@ -98,6 +100,7 @@ export default class RemoveOperation extends MoveOperation {
 	}
 
 	/**
+	 * @inheritDoc
 	 * @returns {engine.model.operation.RemoveOperation}
 	 */
 	clone() {
@@ -112,6 +115,7 @@ export default class RemoveOperation extends MoveOperation {
 	 * @inheritDoc
 	 */
 	_execute() {
+		// Insert "holder" element in graveyard root, if the operation needs it.
 		if ( this._needsHolderElement ) {
 			const graveyard = this.targetPosition.root;
 			const holderElement = new Element( '$graveyardHolder' );
@@ -119,6 +123,7 @@ export default class RemoveOperation extends MoveOperation {
 			graveyard.insertChildren( this.targetPosition.path[ 0 ], holderElement );
 		}
 
+		// Then, execute as a move operation.
 		return super._execute();
 	}
 

+ 2 - 4
packages/ckeditor5-engine/src/model/operation/transform.js

@@ -16,10 +16,8 @@ import isEqual from '../../../utils/lib/lodash/isEqual.js';
 import compareArrays from '../../../utils/comparearrays.js';
 
 /**
- * Transforms given {@link engine.model.operation.Operation operation} by another
- * {@link engine.model.operation.Operation operation} and
- * returns the result of that transformation as an array containing one or more
- * {@link engine.model.operation.Operation operation} elements.
+ * Transforms given {@link engine.model.operation.Operation operation} by another {@link engine.model.operation.Operation operation}
+ * and returns the result of that transformation as an array containing one or more {@link engine.model.operation.Operation operations}.
  *
  * Operations work on specified positions, passed to them when they are created. Whenever {@link engine.model.Document document}
  * changes, we have to reflect those modifications by updating or "transforming" operations which are not yet applied.

+ 59 - 37
packages/ckeditor5-engine/src/model/position.js

@@ -12,8 +12,21 @@ import compareArrays from '../../utils/comparearrays';
 import CKEditorError from '../../utils/ckeditorerror.js';
 
 /**
- * Position in the tree. Position is always located before or after a node.
- * See {@link #path} property for more information.
+ * Represents a position in the model tree.
+ *
+ * Since position in a model is represented by a {@link engine.model.Position#root position root} and
+ * {@link engine.model.Position#path position path} it is possible to create positions placed in non-existing elements.
+ * This requirement is important for {@link engine.model.operation.transfrom operational transformation}.
+ *
+ * Also, {@link engine.model.operation.Operation operations} kept in {@link engine.model.Document#history document history}
+ * are storing positions (and ranges) which were correct when those operations were applied, but may not be correct
+ * after document got changed.
+ *
+ * When changes are applied to model, it may also happen that {@link engine.model.Position#parent position parent} will change
+ * even if position path has not changed. Keep in mind, that if a position leads to non-existing element,
+ * {@link engine.model.Position#parent} and some other properties and methods will throw errors.
+ *
+ * In most cases, position with wrong path is caused by an error in code, but it is sometimes needed, as described above.
  *
  * @memberOf engine.model
  */
@@ -21,9 +34,8 @@ export default class Position {
 	/**
 	 * Creates a position.
 	 *
-	 * @param {engine.model.Element|engine.model.DocumentFragment} root
-	 * Root of the position path. Element (most often a {@link engine.model.RootElement}) or a document fragment.
-	 * @param {Array.<Number>} path Position path. See {@link engine.model.Position#path} property for more information.
+	 * @param {engine.model.Element|engine.model.DocumentFragment} root Root of the position.
+	 * @param {Array.<Number>} path Position path. See {@link engine.model.Position#path}.
 	 */
 	constructor( root, path ) {
 		if ( !( root instanceof Element ) && !( root instanceof DocumentFragment ) ) {
@@ -58,19 +70,31 @@ export default class Position {
 		this.root = root;
 
 		/**
-		 * Position of the node it the tree. Must contain at least one item. For example:
+		 * Position of the node it the tree.
+		 *
+		 * Position can be placed before, after or in a {@link engine.model.Node node} if that node has
+		 * {@link engine.model.Node#offsetSize} greater than `1`. Items in position path are
+		 * {@link engine.model.Node#startOffset starting offsets} of position ancestors, starting from direct root children,
+		 * down to the position offset in it's parent.
 		 *
-		 *		 root
-		 *		  |- p         Before: [ 0 ]       After: [ 1 ]
-		 *		  |- ul        Before: [ 1 ]       After: [ 2 ]
-		 *		     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
-		 *		     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
-		 *		     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
-		 *		     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
-		 *		     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
-		 *		        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
-		 *		        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
-		 *		        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
+		 *		 ROOT
+		 *		  |- P            before: [ 0 ]         after: [ 1 ]
+		 *		  |- UL           before: [ 1 ]         after: [ 2 ]
+		 *		     |- LI        before: [ 1, 0 ]      after: [ 1, 1 ]
+		 *		     |  |- foo    before: [ 1, 0, 0 ]   after: [ 1, 0, 3 ]
+		 *		     |- LI        before: [ 1, 1 ]      after: [ 1, 2 ]
+		 *		        |- bar    before: [ 1, 1, 0 ]   after: [ 1, 1, 3 ]
+		 *
+		 * `foo` and `bar` are representing {@link engine.model.Text text nodes}. Since text nodes has offset size
+		 * greater than `1` you can place position offset between their start and end:
+		 *
+		 *		 ROOT
+		 *		  |- P
+		 *		  |- UL
+		 *		     |- LI
+		 *		     |  |- f^o|o  ^ has path: [ 1, 0, 1 ]   | has path: [ 1, 0, 2 ]
+		 *		     |- LI
+		 *		        |- b^a|r  ^ has path: [ 1, 1, 1 ]   | has path: [ 1, 1, 2 ]
 		 *
 		 * @member {Array.<Number>} engine.model.Position#path
 		 */
@@ -98,9 +122,9 @@ export default class Position {
 	}
 
 	/**
-	 * Offset at which the position is located in the {@link #parent}.
+	 * Offset at which this position is located in it's {@link engine.model.Position#parent parent}. It is equal
+	 * to the last item in position {@link engine.model.Position#path path}.
 	 *
-	 * @readonly
 	 * @type {Number}
 	 */
 	get offset() {
@@ -108,8 +132,6 @@ export default class Position {
 	}
 
 	/**
-	 * Sets offset in the parent, which is the last element of the path.
-	 *
 	 * @param {Number} newOffset
 	 */
 	set offset( newOffset ) {
@@ -167,20 +189,22 @@ export default class Position {
 	}
 
 	/**
-	 * Returns the path to the parent, which is the {@link engine.model.Position#path} without the last element.
+	 * Returns a path to this position's parent. Parent path is equal to position {@link engine.model.Position#path path}
+	 * but without the last item.
 	 *
 	 * This method returns the parent path even if the parent does not exists.
 	 *
-	 * @returns {Number[]} Path to the parent.
+	 * @returns {Array.<Number>} Path to the parent.
 	 */
 	getParentPath() {
 		return this.path.slice( 0, -1 );
 	}
 
 	/**
-	 * Returns a new instance of Position with offset incremented by `shift` value.
+	 * Returns a new instance of `Position`, that has same {@link engine.model.Position#parent parent} but it's offset
+	 * is shifted by `shift` value (can be a negative value).
 	 *
-	 * @param {Number} shift How position offset should get changed. Accepts negative values.
+	 * @param {Number} shift Offset shift. Can be a negative value.
 	 * @returns {engine.model.Position} Shifted position.
 	 */
 	getShiftedBy( shift ) {
@@ -193,7 +217,7 @@ export default class Position {
 	}
 
 	/**
-	 * Returns this position after being updated by removing `howMany` nodes starting from `deletePosition`.
+	 * Returns a copy of this position that is updated by removing `howMany` nodes starting from `deletePosition`.
 	 * It may happen that this position is in a removed node. If that is the case, `null` is returned instead.
 	 *
 	 * @protected
@@ -242,7 +266,7 @@ export default class Position {
 	}
 
 	/**
-	 * Returns this position after being updated by inserting `howMany` nodes at `insertPosition`.
+	 * Returns a copy of this position that is updated by inserting `howMany` nodes at `insertPosition`.
 	 *
 	 * @protected
 	 * @param {engine.model.Position} insertPosition Position where nodes are inserted.
@@ -282,7 +306,7 @@ export default class Position {
 	}
 
 	/**
-	 * Returns this position after being updated by moving `howMany` nodes from `sourcePosition` to `targetPosition`.
+	 * Returns a copy of this position that is updated by moving `howMany` nodes from `sourcePosition` to `targetPosition`.
 	 *
 	 * @protected
 	 * @param {engine.model.Position} sourcePosition Position before the first element to move.
@@ -529,10 +553,9 @@ export default class Position {
 	}
 
 	/**
-	 * Creates a new position from the parent element and the offset in that element.
+	 * Creates a new position from the parent element and an offset in that element.
 	 *
-	 * @param {engine.model.Element|engine.model.DocumentFragment} parent Position's parent element or
-	 * document fragment.
+	 * @param {engine.model.Element|engine.model.DocumentFragment} parent Position's parent.
 	 * @param {Number} offset Position's offset.
 	 * @returns {engine.model.Position}
 	 */
@@ -543,7 +566,7 @@ export default class Position {
 			 *
 			 * @error position-parent-incorrect
 			 */
-			throw new CKEditorError( 'position-parent-incorrect: Position parent have to be a model element or model document fragment.' );
+			throw new CKEditorError( 'position-parent-incorrect: Position parent have to be a element or document fragment.' );
 		}
 
 		const path = parent.getPath();
@@ -554,7 +577,7 @@ export default class Position {
 	}
 
 	/**
-	 * Creates and returns a new instance of Position, which is equal to passed position.
+	 * Creates a new position, which is equal to passed position.
 	 *
 	 * @param {engine.model.Position} position Position to be cloned.
 	 * @returns {engine.model.Position}
@@ -564,11 +587,10 @@ export default class Position {
 	}
 
 	/**
-	 * Creates Element object from deserilized object, ie. from parsed JSON string.
+	 * Creates a `Position` instance from given plain object (i.e. parsed JSON string).
 	 *
-	 * @param {Object} json Deserialized JSON object.
-	 * @param {engine.model.Document} doc Document on which this operation will be applied.
-	 * @returns {engine.model.Position}
+	 * @param {Object} json Plain object to be converted to `Position`.
+	 * @returns {engine.model.Position} `Position` instance created using given plain object.
 	 */
 	static fromJSON( json, doc ) {
 		if ( json.root === '$graveyard' ) {

+ 85 - 77
packages/ckeditor5-engine/src/model/range.js

@@ -17,7 +17,8 @@ import compareArrays from '../../utils/comparearrays.js';
 export default class Range {
 	/**
 	 * Creates a range spanning from `start` position to `end` position.
-	 * **Note:** Constructor creates it's own {@link engine.model.Position} instances basing on passed values.
+	 *
+	 * **Note:** Constructor creates it's own {@link engine.model.Position Position} instances basing on passed values.
 	 *
 	 * @param {engine.model.Position} start Start position.
 	 * @param {engine.model.Position} end End position.
@@ -43,20 +44,12 @@ export default class Range {
 	/**
 	 * Returns an iterator that iterates over all {@link engine.model.Item items} that are in this range and returns
 	 * them together with additional information like length or {@link engine.model.Position positions},
-	 * grouped as {@link engine.model.TreeWalkerValue}. It iterates over all {@link engine.model.TextProxy texts}
-	 * that are inside the range and all the {@link engine.model.Element}s we enter into when iterating over this
-	 * range.
-	 *
-	 * **Note:** iterator will not return a parent node of start position. This is in contrary to
-	 * {@link engine.model.TreeWalker} which will return that node with `'elementEnd'` type. Iterator also
-	 * returns each {@link engine.model.Element} once, while simply used {@link engine.model.TreeWalker} might
-	 * return it twice: for `'elementStart'` and `'elementEnd'`.
+	 * grouped as {@link engine.model.TreeWalkerValue}. It iterates over all {@link engine.model.TextProxy text contents}
+	 * that are inside the range and all the {@link engine.model.Element}s that are entered into when iterating over this range.
 	 *
-	 * **Note:** because iterator does not return {@link engine.model.TreeWalkerValue values} with the type of
-	 * `'elementEnd'`, you can use {@link engine.model.TreeWalkerValue.previousPosition} as a position before the
-	 * item.
+	 * This iterator uses {@link engine.model.TreeWalker} with `boundaries` set to this range and `ignoreElementEnd` option
+	 * set to `true`.
 	 *
-	 * @see engine.model.TreeWalker
 	 * @returns {Iterable.<engine.model.TreeWalkerValue>}
 	 */
 	*[ Symbol.iterator ]() {
@@ -64,7 +57,8 @@ export default class Range {
 	}
 
 	/**
-	 * Returns whether the range is collapsed, that is it start and end positions are equal.
+	 * Returns whether the range is collapsed, that is if {@link engine.model.Range#start start} and
+	 * {@link engine.model.Range#end end} positions are equal.
 	 *
 	 * @type {Boolean}
 	 */
@@ -73,7 +67,8 @@ export default class Range {
 	}
 
 	/**
-	 * Returns whether this range is flat, that is if start position and end position are in the same parent.
+	 * Returns whether this range is flat, that is if {@link engine.model.Range#start start} position and
+	 * {@link engine.model.Range#end end} position are in the same {@link engine.model.Position#parent parent}.
 	 *
 	 * @type {Boolean}
 	 */
@@ -82,7 +77,10 @@ export default class Range {
 	}
 
 	/**
-	 * Returns whether this range has any nodes in it.
+	 * Returns whether this range has no nodes in it, that is if {@link engine.model.Range#start start} position and
+	 * {@link engine.model.Range#end end} position are {@link engine.model.Position#isTouching touching}.
+	 *
+	 * **Note:** A range may be empty, but not {@link engine.model.Range#isCollapsed collapsed}.
 	 *
 	 * @type {Boolean}
 	 */
@@ -93,19 +91,17 @@ export default class Range {
 	/**
 	 * Range root element.
 	 *
-	 * Equals to the root of start position (which should be same as root of end position).
-	 *
-	 * @type {engine.model.RootElement|engine.model.DocumentFragment}
+	 * @type {engine.model.Element|engine.model.DocumentFragment}
 	 */
 	get root() {
 		return this.start.root;
 	}
 
 	/**
-	 * Checks whether this contains given {@link engine.model.Position position}.
+	 * Checks whether this range contains given {@link engine.model.Position position}.
 	 *
 	 * @param {engine.model.Position} position Position to check.
-	 * @returns {Boolean} True if given {@link engine.model.Position position} is contained.
+	 * @returns {Boolean} `true` if given {@link engine.model.Position position} is contained in this range, `false` otherwise.
 	 */
 	containsPosition( position ) {
 		return position.isAfter( this.start ) && position.isBefore( this.end );
@@ -115,15 +111,36 @@ export default class Range {
 	 * Checks whether this range contains given {@link engine.model.Range range}.
 	 *
 	 * @param {engine.model.Range} otherRange Range to check.
-	 * @returns {Boolean} True if given {@link engine.model.Range range} boundaries are contained by this range.
+	 * @returns {Boolean} `true` if given {@link engine.model.Range range} boundaries are contained by this range, `false` otherwise.
 	 */
 	containsRange( otherRange ) {
 		return this.containsPosition( otherRange.start ) && this.containsPosition( otherRange.end );
 	}
 
 	/**
-	 * Gets a part of this {@link engine.model.Range range} which is not a part of given {@link engine.model.Range range}. Returned
-	 * array contains zero, one or two {@link engine.model.Range ranges}.
+	 * Two ranges are equal if their {@link engine.model.Range#start start} and
+	 * {@link engine.model.Range#end end} positions are equal.
+	 *
+	 * @param {engine.model.Range} otherRange Range to compare with.
+	 * @returns {Boolean} `true` if ranges are equal, `false` otherwise.
+	 */
+	isEqual( otherRange ) {
+		return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
+	}
+
+	/**
+	 * Checks and returns whether this range intersects with given range.
+	 *
+	 * @param {engine.model.Range} otherRange Range to compare with.
+	 * @returns {Boolean} `true` if ranges intersect, `false` otherwise.
+	 */
+	isIntersecting( otherRange ) {
+		return this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start );
+	}
+
+	/**
+	 * Computes which part(s) of this {@link engine.model.Range range} is not a part of given {@link engine.model.Range range}.
+	 * Returned array contains zero, one or two {@link engine.model.Range ranges}.
 	 *
 	 * Examples:
 	 *
@@ -169,8 +186,8 @@ export default class Range {
 	}
 
 	/**
-	 * Returns an intersection of this {@link engine.model.Range range} and given {@link engine.model.Range range}. Intersection
-	 * is a common part of both of those ranges. If ranges has no common part, returns `null`.
+	 * Returns an intersection of this {@link engine.model.Range range} and given {@link engine.model.Range range}.
+	 * Intersection is a common part of both of those ranges. If ranges has no common part, returns `null`.
 	 *
 	 * Examples:
 	 *
@@ -182,7 +199,7 @@ export default class Range {
 	 *		transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
 	 *
 	 * @param {engine.model.Range} otherRange Range to check for intersection.
-	 * @returns {engine.model.Range|null} A common part of given ranges or null if ranges have no common part.
+	 * @returns {engine.model.Range|null} A common part of given ranges or `null` if ranges have no common part.
 	 */
 	getIntersection( otherRange ) {
 		if ( this.isIntersecting( otherRange ) ) {
@@ -211,8 +228,9 @@ export default class Range {
 	}
 
 	/**
-	 * Computes and returns the smallest set of {@link #isFlat flat} ranges, that covers this range in whole.
-	 * Assuming that tree model model structure is ("[" and "]" are range boundaries):
+	 * Computes and returns the smallest set of {@link engine.model.Range#isFlat flat} ranges, that covers this range in whole.
+	 *
+	 * See an example of model structure (`[` and `]` are range boundaries):
 	 *
 	 *		root                                                            root
 	 *		 |- element DIV                         DIV             P2              P3             DIV
@@ -230,8 +248,8 @@ export default class Range {
 	 *		 |   |- element P4
 	 *		 |   |   |- "ipsum"
 	 *
-	 * As it can be seen, letters contained in the range are stloremfoobarse, spread across different parents.
-	 * We are looking for minimal set of {@link #isFlat flat} ranges that contains the same nodes.
+	 * As it can be seen, letters contained in the range are: `stloremfoobarse`, spread across different parents.
+	 * We are looking for minimal set of flat ranges that contains the same nodes.
 	 *
 	 * Minimal flat ranges for above range `( [ 0, 0, 3 ], [ 3, 0, 2 ] )` will be:
 	 *
@@ -240,10 +258,13 @@ export default class Range {
 	 *		( [ 1 ], [ 3 ] ) = element P2, element P3 ("foobar")
 	 *		( [ 3, 0, 0 ], [ 3, 0, 2 ] ) = "se"
 	 *
-	 * **Note:** this method is not returning flat ranges that contain no nodes. It may also happen that not-collapsed
-	 * range will return an empty array of flat ranges.
+	 * **Note:** if an {@link engine.model.Element element} is not contained wholly in this range, it won't be returned
+	 * in any of returned flat ranges. See in an example, how `H` elements at the beginning and at the end of the range
+	 * were omitted. Only it's parts that were wholly in the range were returned.
+	 *
+	 * **Note:** this method is not returning flat ranges that contain no nodes.
 	 *
-	 * @returns {Array.<engine.model.Range>} Array of flat ranges.
+	 * @returns {Array.<engine.model.Range>} Array of flat ranges covering this range.
 	 */
 	getMinimalFlatRanges() {
 		let ranges = [];
@@ -286,7 +307,7 @@ export default class Range {
 	}
 
 	/**
-	 * Creates a {@link engine.model.TreeWalker} instance with this range as a boundary.
+	 * Creates a {@link engine.model.TreeWalker TreeWalker} instance with this range as a boundary.
 	 *
 	 * @param {Object} options Object with configuration options. See {@link engine.model.TreeWalker}.
 	 * @param {engine.model.Position} [options.startPosition]
@@ -302,15 +323,15 @@ export default class Range {
 
 	/**
 	 * Returns an iterator that iterates over all {@link engine.model.Item items} that are in this range and returns
-	 * them. It iterates over all {@link engine.model.CharacterProxy characters} or
-	 * {@link engine.model.TextProxy texts} that are inside the range and all the {@link engine.model.Element}s
-	 * we enter into when iterating over this range. Note that it use {@link engine.model.TreeWalker} with the
-	 * {@link engine.model.TreeWalker#ignoreElementEnd ignoreElementEnd} option set to true.
+	 * them.
+	 *
+	 * This method uses {@link engine.model.TreeWalker} with `boundaries` set to this range and `ignoreElementEnd` option
+	 * set to `true`. However it returns only {@link engine.model.Item model items}, not {@link engine.model.TreeWalkerValue}.
+	 *
+	 * You may specify additional options for the tree walker. See {@link engine.model.TreeWalker} for
+	 * a full list of available options.
 	 *
 	 * @param {Object} options Object with configuration options. See {@link engine.model.TreeWalker}.
-	 * @param {engine.model.Position} [options.startPosition]
-	 * @param {Boolean} [options.singleCharacters=false]
-	 * @param {Boolean} [options.shallow=false]
 	 * @returns {Iterable.<engine.model.Item>}
 	 */
 	*getItems( options = {} ) {
@@ -328,9 +349,13 @@ export default class Range {
 	 * Returns an iterator that iterates over all {@link engine.model.Position positions} that are boundaries or
 	 * contained in this range.
 	 *
+	 * This method uses {@link engine.model.TreeWalker} with `boundaries` set to this range. However it returns only
+	 * {@link engine.model.Position positions}, not {@link engine.model.TreeWalkerValue}.
+	 *
+	 * You may specify additional options for the tree walker. See {@link engine.model.TreeWalker} for
+	 * a full list of available options.
+	 *
 	 * @param {Object} options Object with configuration options. See {@link engine.model.TreeWalker}.
-	 * @param {Boolean} [options.singleCharacters=false]
-	 * @param {Boolean} [options.shallow=false]
 	 * @returns {Iterable.<engine.model.Position>}
 	 */
 	*getPositions( options = {} ) {
@@ -346,7 +371,7 @@ export default class Range {
 	}
 
 	/**
-	 * Returns an array containing one or two {engine.model.Range ranges} that are a result of transforming this
+	 * Returns an array containing one or two {@link engine.model.Range ranges} that are a result of transforming this
 	 * {@link engine.model.Range range} by inserting `howMany` nodes at `insertPosition`. Two {@link engine.model.Range ranges} are
 	 * returned if the insertion was inside this {@link engine.model.Range range} and `spread` is set to `true`.
 	 *
@@ -407,9 +432,10 @@ export default class Range {
 	}
 
 	/**
-	 * Returns an array containing {engine.model.Range ranges} that are a result of transforming this
+	 * Returns an array containing {@link engine.model.Range ranges} that are a result of transforming this
 	 * {@link engine.model.Range range} by moving `howMany` nodes from `sourcePosition` to `targetPosition`.
 	 *
+	 * @protected
 	 * @param {engine.model.Position} sourcePosition Position from which nodes are moved.
 	 * @param {engine.model.Position} targetPosition Position to where nodes are moved.
 	 * @param {Number} howMany How many nodes are moved.
@@ -465,47 +491,30 @@ export default class Range {
 	}
 
 	/**
-	 * Two ranges equal if their start and end positions equal.
-	 *
-	 * @param {engine.model.Range} otherRange Range to compare with.
-	 * @returns {Boolean} True if ranges equal.
-	 */
-	isEqual( otherRange ) {
-		return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
-	}
-
-	/**
-	 * Checks and returns whether this range intersects with given range.
-	 *
-	 * @param {engine.model.Range} otherRange Range to compare with.
-	 * @returns {Boolean} True if ranges intersect.
-	 */
-	isIntersecting( otherRange ) {
-		return this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start );
-	}
-
-	/**
-	 * Creates a range inside an element which starts before the first child and ends after the last child.
+	 * Creates a range inside an {@link engine.model.Element element} which starts before the first child of
+	 * that element and ends after the last child of that element.
 	 *
 	 * @param {engine.model.Element} element Element which is a parent for the range.
-	 * @returns {engine.model.Range} Created range.
+	 * @returns {engine.model.Range}
 	 */
 	static createFromElement( element ) {
 		return this.createFromParentsAndOffsets( element, 0, element, element.getMaxOffset() );
 	}
 
 	/**
-	 * Creates a range on given element only. The range starts just before the element and ends before the first child of the element.
+	 * Creates a range on given {@link engine.model.Element element} only. The range starts directly before that element
+	 * and ends before the first child of that element.
 	 *
 	 * @param {engine.model.Element} element Element on which range should be created.
-	 * @returns {engine.model.Range} Created range.
+	 * @returns {engine.model.Range}
 	 */
 	static createOnElement( element ) {
 		return this.createFromParentsAndOffsets( element.parent, element.startOffset, element, 0 );
 	}
 
 	/**
-	 * Creates a new range spreading from specified position to the same position moved by given shift.
+	 * Creates a new range, spreading from specified {@link engine.model.Position position} to a position moved by
+	 * given `shift`. If `shift` is a negative value, shifted position is treated as the beginning of the range.
 	 *
 	 * @param {engine.model.Position} position Beginning of the range.
 	 * @param {Number} shift How long the range should be.
@@ -525,7 +534,7 @@ export default class Range {
 	 * @param {Number} startOffset Start position offset.
 	 * @param {engine.model.Element} endElement End position parent element.
 	 * @param {Number} endOffset End position offset.
-	 * @returns {engine.model.Range} Created range.
+	 * @returns {engine.model.Range}
 	 */
 	static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
 		return new this(
@@ -535,7 +544,7 @@ export default class Range {
 	}
 
 	/**
-	 * Creates and returns a new instance of Range which is equal to passed range.
+	 * Creates a new instance of `Range` which is equal to passed range.
 	 *
 	 * @param {engine.model.Range} range Range to clone.
 	 * @returns {engine.model.Range}
@@ -545,11 +554,10 @@ export default class Range {
 	}
 
 	/**
-	 * Creates Range from deserilized object, ie. from parsed JSON string.
+	 * Creates a `Range` instance from given plain object (i.e. parsed JSON string).
 	 *
-	 * @param {Object} json Deserialized JSON object.
-	 * @param {engine.model.Document} doc Document on which this operation will be applied.
-	 * @returns {engine.model.Range}
+	 * @param {Object} json Plain object to be converted to `Range`.
+	 * @returns {engine.model.Element} `Range` instance created using given plain object.
 	 */
 	static fromJSON( json, doc ) {
 		return new this( Position.fromJSON( json.start, doc ), Position.fromJSON( json.end, doc ) );

+ 167 - 134
packages/ckeditor5-engine/src/model/selection.js

@@ -14,7 +14,8 @@ import toMap from '../../utils/tomap.js';
 
 /**
  * `Selection` is a group of {@link engine.model.Range ranges} which has a direction specified by
- * {@link engine.model.Selection#anchor anchor} and {@link engine.model.Selection#focus focus}.
+ * {@link engine.model.Selection#anchor anchor} and {@link engine.model.Selection#focus focus}. Additionally,
+ * `Selection` may have it's own attributes.
  *
  * @memberOf engine.model
  */
@@ -32,7 +33,7 @@ export default class Selection {
 		this._lastRangeBackward = false;
 
 		/**
-		 * Stores all ranges that are selected.
+		 * Stores selection ranges.
 		 *
 		 * @protected
 		 * @member {Array.<engine.model.Range>} engine.model.Selection#_ranges
@@ -49,90 +50,15 @@ export default class Selection {
 	}
 
 	/**
-	 * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
-	 *
-	 * @param {String} key Key of attribute to look for.
-	 * @returns {*} Attribute value or `undefined`.
-	 */
-	getAttribute( key ) {
-		return this._attrs.get( key );
-	}
-
-	/**
-	 * Returns iterator that iterates over this selection attributes.
-	 *
-	 * @returns {Iterable.<*>}
-	 */
-	getAttributes() {
-		return this._attrs[ Symbol.iterator ]();
-	}
-
-	/**
-	 * Checks if the selection has an attribute for given key.
-	 *
-	 * @param {String} key Key of attribute to check.
-	 * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.
-	 */
-	hasAttribute( key ) {
-		return this._attrs.has( key );
-	}
-
-	/**
-	 * Removes all attributes from the selection.
-	 *
-	 * @fires engine.model.Selection#change:attribute
-	 */
-	clearAttributes() {
-		this._attrs.clear();
-
-		this.fire( 'change:attribute' );
-	}
-
-	/**
-	 * Removes an attribute with given key from the selection.
-	 *
-	 * @fires engine.model.Selection#change:attribute
-	 * @param {String} key Key of attribute to remove.
-	 */
-	removeAttribute( key ) {
-		this._attrs.delete( key );
-
-		this.fire( 'change:attribute' );
-	}
-
-	/**
-	 * Sets attribute on the selection. If attribute with the same key already is set, it overwrites its values.
-	 *
-	 * @fires engine.model.Selection#change:attribute
-	 * @param {String} key Key of attribute to set.
-	 * @param {*} value Attribute value.
-	 */
-	setAttribute( key, value ) {
-		this._attrs.set( key, value );
-
-		this.fire( 'change:attribute' );
-	}
-
-	/**
-	 * Removes all attributes from the selection and sets given attributes.
-	 *
-	 * @fires engine.model.Selection#change:attribute
-	 * @param {Iterable|Object} attrs Iterable object containing attributes to be set.
-	 */
-	setAttributesTo( attrs ) {
-		this._attrs = toMap( attrs );
-
-		this.fire( 'change:attribute' );
-	}
-
-	/**
-	 * Selection anchor. Anchor may be described as a position where the selection starts. Together with
-	 * {@link engine.model.Selection#focus} they define the direction of selection, which is important
-	 * when expanding/shrinking selection. Anchor is always the start or end of the most recent added range.
+	 * Selection anchor. Anchor may be described as a position where the most recent part of the selection starts.
+	 * Together with {@link engine.model.Selection#focus} they define the direction of selection, which is important
+	 * when expanding/shrinking selection. Anchor is always {@link engine.model.Range#start start} or
+	 * {@link engine.model.Range#end end} position of the most recently added range.
 	 *
 	 * Is set to `null` if there are no ranges in selection.
 	 *
 	 * @see engine.model.Selection#focus
+	 * @readonly
 	 * @type {engine.model.Position|null}
 	 */
 	get anchor() {
@@ -151,6 +77,7 @@ export default class Selection {
 	 * Is set to `null` if there are no ranges in selection.
 	 *
 	 * @see engine.model.Selection#anchor
+	 * @readonly
 	 * @type {engine.model.Position|null}
 	 */
 	get focus() {
@@ -167,6 +94,7 @@ export default class Selection {
 	 * Returns whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
 	 * collapsed.
 	 *
+	 * @readonly
 	 * @type {Boolean}
 	 */
 	get isCollapsed() {
@@ -183,7 +111,7 @@ export default class Selection {
 	 * Returns number of ranges in selection.
 	 *
 	 * @type {Number}
-     */
+	 */
 	get rangeCount() {
 		return this._ranges.length;
 	}
@@ -198,28 +126,25 @@ export default class Selection {
 	}
 
 	/**
-	 * Adds a range to the selection. Added range is copied. This means that passed range is not saved in `Selection`
-	 * instance and operating on it will not change `Selection` state.
-	 *
-	 * Accepts a flag describing in which way the selection is made - passed range might be selected from
-	 * {@link engine.model.Range#start start} to {@link engine.model.Range#end end} or from {@link engine.model.Range#end end}
-	 * to {@link engine.model.Range#start start}. The flag is used to set {@link engine.model.Selection#anchor} and
-	 * {@link engine.model.Selection#focus} properties.
+	 * Checks whether, this selection is equal to given selection. Selections equal if they have the same ranges and directions.
 	 *
-	 * @fires engine.model.Selection#change:range
-	 * @param {engine.model.Range} range Range to add.
-	 * @param {Boolean} [isBackward] Flag describing if added range was selected forward - from start to end (`false`)
-	 * or backward - from end to start (`true`). Defaults to `false`.
+	 * @param {engine.model.Selection} otherSelection Selection to compare with.
+	 * @returns {Boolean} `true` if selections are equal, `false` otherwise.
 	 */
-	addRange( range, isBackward ) {
-		if ( !( range instanceof Range ) ) {
-			throw new CKEditorError( 'selection-invalid-range: Invalid Range.' );
+	isEqual( otherSelection ) {
+		const rangeCount = this.rangeCount;
+
+		if ( rangeCount != otherSelection.rangeCount ) {
+			return false;
 		}
 
-		this._pushRange( range );
-		this._lastRangeBackward = !!isBackward;
+		for ( let i = 0; i < this.rangeCount; i++ ) {
+			if ( !this._ranges[ i ].isEqual( otherSelection._ranges[ i ] ) ) {
+				return false;
+			}
+		}
 
-		this.fire( 'change:range' );
+		return this.isBackward === otherSelection.isBackward;
 	}
 
 	/**
@@ -245,9 +170,7 @@ export default class Selection {
 	getFirstRange() {
 		let first = null;
 
-		for ( let i = 0; i < this._ranges.length; i++ ) {
-			let range = this._ranges[ i ];
-
+		for ( let range of this._ranges ) {
 			if ( !first || range.start.isBefore( first.start ) ) {
 				first = range;
 			}
@@ -258,7 +181,7 @@ export default class Selection {
 
 	/**
 	 * Returns the first position in the selection. First position is the position that {@link engine.model.Position#isBefore is before}
-	 * any other position in the selection ranges.
+	 * any other position in the selection.
 	 *
 	 * Returns `null` if there are no ranges in selection.
 	 *
@@ -271,7 +194,28 @@ export default class Selection {
 	}
 
 	/**
-	 * Removes all ranges that were added to the selection. Fires update event.
+	 * Adds a range to this selection. Added range is copied. This means that passed range is not saved in `Selection`
+	 * instance and operating on it will not change `Selection` state.
+	 *
+	 * Accepts a flag describing in which way the selection is made - passed range might be selected from
+	 * {@link engine.model.Range#start start} to {@link engine.model.Range#end end} or from {@link engine.model.Range#end end}
+	 * to {@link engine.model.Range#start start}. The flag is used to set {@link engine.model.Selection#anchor} and
+	 * {@link engine.model.Selection#focus} properties.
+	 *
+	 * @fires engine.model.Selection#change:range
+	 * @param {engine.model.Range} range Range to add.
+	 * @param {Boolean} [isBackward=false] Flag describing if added range was selected forward - from start to end (`false`)
+	 * or backward - from end to start (`true`).
+	 */
+	addRange( range, isBackward = false ) {
+		this._pushRange( range );
+		this._lastRangeBackward = !!isBackward;
+
+		this.fire( 'change:range' );
+	}
+
+	/**
+	 * Removes all ranges that were added to the selection.
 	 *
 	 * @fires engine.model.Selection#change:range
 	 */
@@ -283,13 +227,14 @@ export default class Selection {
 
 	/**
 	 * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
-	 * is treated like the last added range and is used to set {@link #anchor} and {@link #focus}. Accepts a flag
-	 * describing in which way the selection is made (see {@link #addRange}).
+	 * is treated like the last added range and is used to set {@link engine.model.Selection#anchor} and
+	 * {@link engine.model.Selection#focus}. Accepts a flag describing in which direction the selection is made
+	 * (see {@link engine.model.Selection#addRange}).
 	 *
 	 * @fires engine.model.Selection#change:range
-	 * @param {Iterable.<engine.model.Range>} newRanges Iterable set of ranges that should be set.
-	 * @param {Boolean} [isLastBackward] Flag describing if last added range was selected forward - from start to end (`false`)
-	 * or backward - from end to start (`true`). Defaults to `false`.
+	 * @param {Iterable.<engine.model.Range>} newRanges Ranges to set.
+	 * @param {Boolean} [isLastBackward=false] Flag describing if last added range was selected forward - from start to end (`false`)
+	 * or backward - from end to start (`true`).
 	 */
 	setRanges( newRanges, isLastBackward ) {
 		this._ranges = [];
@@ -313,28 +258,117 @@ export default class Selection {
 	 * The location can be specified in the same form as {@link engine.model.Position.createAt} parameters.
 	 *
 	 * @fires engine.model.Selection#change:range
-	 * @param {engine.model.Node|engine.model.Position} nodeOrPosition
+	 * @param {engine.model.Item|engine.model.Position} itemOrPosition
 	 * @param {Number|'end'|'before'|'after'} [offset=0] Offset or one of the flags. Used only when
-	 * first parameter is a node.
+	 * first parameter is a {@link engine.model.Item model item}.
 	 */
-	collapse( nodeOrPosition, offset ) {
-		const pos = Position.createAt( nodeOrPosition, offset );
+	collapse( itemOrPosition, offset ) {
+		const pos = Position.createAt( itemOrPosition, offset );
 		const range = new Range( pos, pos );
 
 		this.setRanges( [ range ] );
 	}
 
 	/**
-	 * Sets {@link engine.model.Selection#focus} in the specified location.
+	 * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
+	 *
+	 * @param {String} key Key of attribute to look for.
+	 * @returns {*} Attribute value or `undefined`.
+	 */
+	getAttribute( key ) {
+		return this._attrs.get( key );
+	}
+
+	/**
+	 * Returns iterator that iterates over this selection's attributes.
+	 *
+	 * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
+	 * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
+	 *
+	 * @returns {Iterable.<*>}
+	 */
+	getAttributes() {
+		return this._attrs.entries();
+	}
+
+	/**
+	 * Returns iterator that iterates over this selection's attribute keys.
+	 *
+	 * @returns {Iterator.<String>}
+	 */
+	getAttributeKeys() {
+		return this._attrs.keys();
+	}
+
+	/**
+	 * Checks if the selection has an attribute for given key.
+	 *
+	 * @param {String} key Key of attribute to check.
+	 * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.
+	 */
+	hasAttribute( key ) {
+		return this._attrs.has( key );
+	}
+
+	/**
+	 * Removes all attributes from the selection.
+	 *
+	 * @fires engine.model.Selection#change:attribute
+	 */
+	clearAttributes() {
+		this._attrs.clear();
+
+		this.fire( 'change:attribute' );
+	}
+
+	/**
+	 * Removes an attribute with given key from the selection.
+	 *
+	 * @fires engine.model.Selection#change:attribute
+	 * @param {String} key Key of attribute to remove.
+	 */
+	removeAttribute( key ) {
+		this._attrs.delete( key );
+
+		this.fire( 'change:attribute' );
+	}
+
+	/**
+	 * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.
+	 *
+	 * @fires engine.model.Selection#change:attribute
+	 * @param {String} key Key of attribute to set.
+	 * @param {*} value Attribute value.
+	 */
+	setAttribute( key, value ) {
+		this._attrs.set( key, value );
+
+		this.fire( 'change:attribute' );
+	}
+
+	/**
+	 * Removes all attributes from the selection and sets given attributes.
+	 *
+	 * @fires engine.model.Selection#change:attribute
+	 * @param {Iterable|Object} attrs Iterable object containing attributes to be set.
+	 */
+	setAttributesTo( attrs ) {
+		this._attrs = toMap( attrs );
+
+		this.fire( 'change:attribute' );
+	}
+
+	/**
+	 * Sets {@link engine.model.Selection#focus} to the specified location.
 	 *
 	 * The location can be specified in the same form as {@link engine.model.Position.createAt} parameters.
 	 *
 	 * @fires engine.model.Selection#change:range
-	 * @param {engine.model.Node|engine.model.Position} nodeOrPosition
+	 * @param {engine.model.Item|engine.model.Position} itemOrPosition
 	 * @param {Number|'end'|'before'|'after'} [offset=0] Offset or one of the flags. Used only when
-	 * first parameter is a node.
+	 * first parameter is a {@link engine.model.Item model item}.
 	 */
-	setFocus( nodeOrPosition, offset ) {
+	setFocus( itemOrPosition, offset ) {
 		if ( this.anchor === null ) {
 			/**
 			 * Cannot set selection focus if there are no ranges in selection.
@@ -344,7 +378,7 @@ export default class Selection {
 			throw new CKEditorError( 'selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.' );
 		}
 
-		const newFocus = Position.createAt( nodeOrPosition, offset );
+		const newFocus = Position.createAt( itemOrPosition, offset );
 
 		if ( newFocus.compareWith( this.focus ) == 'same' ) {
 			return;
@@ -364,8 +398,8 @@ export default class Selection {
 	}
 
 	/**
-	 * Creates and returns an instance of {@link engine.model.Selection} that is a clone of given selection,
-	 * meaning that it has same ranges and same direction as it.
+	 * Creates and returns an instance of `Selection` that is a clone of given selection, meaning that it has same
+	 * ranges and same direction as this selection.
 	 *
 	 * @params {engine.model.Selection} otherSelection Selection to be cloned.
 	 * @returns {engine.model.Selection} `Selection` instance that is a clone of given selection.
@@ -377,9 +411,20 @@ export default class Selection {
 		return selection;
 	}
 
+	/**
+	 * Adds given range to internal {@link engine.model.Selection#_ranges ranges array}. Throws an error
+	 * if given range is intersecting with any range that is already stored in this selection.
+	 *
+	 * @protected
+	 * @param {engine.model.Range} range Range to add.
+	 */
+	_pushRange( range ) {
+		this._checkRange( range );
+		this._ranges.push( Range.createFromRange( range ) );
+	}
+
 	/**
 	 * Checks if given range intersects with ranges that are already in the selection. Throws an error if it does.
-	 * This method is extracted from {@link engine.model.Selection#_pushRange } so it is easier to override it.
 	 *
 	 * @param {engine.model.Range} range Range to check.
 	 * @protected
@@ -410,18 +455,6 @@ export default class Selection {
 	_popRange() {
 		this._ranges.pop();
 	}
-
-	/**
-	 * Adds given range to internal {@link engine.model.Selection#_ranges ranges array}. Throws an error
-	 * if given range is intersecting with any range that is already stored in this selection.
-	 *
-	 * @protected
-	 * @param {engine.model.Range} range Range to add.
-	 */
-	_pushRange( range ) {
-		this._checkRange( range );
-		this._ranges.push( Range.createFromRange( range ) );
-	}
 }
 
 mix( Selection, EmitterMixin );